@@ -243,6 +259,18 @@ export function ColorOptionSetting({label, onClick, selectedName, buttons, layou
);
}
+export function ColorOptionSettingBeta({label, onClick, selectedName, buttons, layout, dataTestId}) {
+ return (
+
+ );
+}
+
export function ColorPickerSetting({label, isExpanded, onSwatchChange, onPickerChange, onTogglePicker, value, swatches, eyedropper, hasTransparentOption, dataTestId}) {
const mappedPicker = (event) => {
onTogglePicker(true);
@@ -286,15 +314,81 @@ export function ColorPickerSetting({label, isExpanded, onSwatchChange, onPickerC
);
}
-export function MediaUploadSetting({className, label, hideLabel, onFileChange, isDraggedOver, placeholderRef, src, alt, isLoading, errors = [], progress, onRemoveMedia, icon, desc = '', size, stacked, borderStyle, mimeTypes, isPinturaEnabled, openImageEditor, setFileInputRef}) {
+export function ColorPickerSettingBeta({label, isExpanded, onSwatchChange, onPickerChange, onTogglePicker, value, swatches, eyedropper, hasTransparentOption, dataTestId, customToolbarContent, children}) {
+ const markClickedInside = (event) => {
+ event.stopPropagation();
+ };
+
return (
-
-
{label}
+
+
+
{label}
+
+
+
+ {children}
+
+
+
+
+ );
+}
+
+export function MediaUploadSetting({className, label, hideLabel, onFileChange, isDraggedOver, placeholderRef, src, alt, isLoading, errors = [], progress, onRemoveMedia, icon, desc, size, type, stacked, borderStyle, mimeTypes, isPinturaEnabled, openImageEditor, setFileInputRef}) {
+ return (
+
+ );
+}
+
+export function MediaUploadSettingBeta({className, label, hideLabel, onFileChange, isDraggedOver, placeholderRef, src, alt, isLoading, errors = [], progress, onRemoveMedia, icon, desc, size, type, stacked, borderStyle, mimeTypes, isPinturaEnabled, openImageEditor, setFileInputRef}) {
+ return (
+
+
{label}
+
+
diff --git a/packages/koenig-lexical/src/components/ui/cards/CallToActionCard.jsx b/packages/koenig-lexical/src/components/ui/cards/CallToActionCard.jsx
index 7efae0942e..e37c5897ec 100644
--- a/packages/koenig-lexical/src/components/ui/cards/CallToActionCard.jsx
+++ b/packages/koenig-lexical/src/components/ui/cards/CallToActionCard.jsx
@@ -7,7 +7,7 @@ import ReplacementStringsPlugin from '../../../plugins/ReplacementStringsPlugin.
import clsx from 'clsx';
import defaultTheme from '../../../themes/default.js';
import {Button} from '../Button.jsx';
-import {ButtonGroupSetting, ColorOptionSetting, ColorPickerSetting, InputSetting, InputUrlSetting, MediaUploadSetting, SettingsPanel, ToggleSetting} from '../SettingsPanel.jsx';
+import {ButtonGroupSettingBeta, ColorOptionSettingBeta, ColorPickerSettingBeta, InputSetting, InputUrlSetting, MediaUploadSettingBeta, SettingsPanel, ToggleSetting} from '../SettingsPanel.jsx';
import {ReadOnlyOverlay} from '../ReadOnlyOverlay.jsx';
import {RestrictContentPlugin} from '../../../index.js';
import {VisibilitySettings} from '../VisibilitySettings.jsx';
@@ -21,7 +21,9 @@ export const CALLTOACTION_COLORS = {
blue: 'bg-blue/10 border-transparent',
green: 'bg-green/10 border-transparent',
yellow: 'bg-yellow/10 border-transparent',
- red: 'bg-red/10 border-transparent'
+ red: 'bg-red/10 border-transparent',
+ pink: 'bg-pink/10 border-transparent',
+ purple: 'bg-purple/10 border-transparent'
};
const sponsoredLabelTheme = {
@@ -43,27 +45,37 @@ export const callToActionColorPicker = [
{
label: 'Grey',
name: 'grey',
- color: 'bg-grey/15 border-black/[.08] dark:border-white/10'
+ color: 'bg-grey/20 border-black/[.08] dark:border-white/10'
},
{
label: 'Blue',
name: 'blue',
- color: 'bg-blue/15 border-black/[.08] dark:border-white/10'
+ color: 'bg-blue/20 border-black/[.08] dark:border-white/10'
},
{
label: 'Green',
name: 'green',
- color: 'bg-green/15 border-black/[.08] dark:border-white/10'
+ color: 'bg-green/20 border-black/[.08] dark:border-white/10'
},
{
label: 'Yellow',
name: 'yellow',
- color: 'bg-yellow/15 border-black/[.08] dark:border-white/10'
+ color: 'bg-yellow/20 border-black/[.08] dark:border-white/10'
},
{
label: 'Red',
name: 'red',
- color: 'bg-red/15 border-black/[.08] dark:border-white/10'
+ color: 'bg-red/20 border-black/[.08] dark:border-white/10'
+ },
+ {
+ label: 'Pink',
+ name: 'pink',
+ color: 'bg-pink/20 border-black/[0.08] dark:border-white/10'
+ },
+ {
+ label: 'Purple',
+ name: 'purple',
+ color: 'bg-purple/20 border-black/[0.08] dark:border-white/10'
}
];
@@ -93,7 +105,8 @@ export function CallToActionCard({
updateHasSponsorLabel = () => {},
updateLayout = () => {},
updateShowButton = () => {},
- toggleVisibility = () => {}
+ toggleVisibility = () => {},
+ imageDragHandler = {}
}) {
const [buttonColorPickerExpanded, setButtonColorPickerExpanded] = useState(false);
@@ -107,13 +120,15 @@ export function CallToActionCard({
label: 'Minimal',
name: 'minimal',
Icon: MinimalLayoutIcon,
- dataTestId: 'minimal-layout'
+ dataTestId: 'minimal-layout',
+ ariaLabel: 'Left-aligned layout with small, square image'
},
{
label: 'Immersive',
name: 'immersive',
Icon: ImmersiveLayoutIcon,
- dataTestId: 'immersive-layout'
+ dataTestId: 'immersive-layout',
+ ariaLabel: 'Center-aligned layout with full-width image and button'
}
];
@@ -123,20 +138,22 @@ export function CallToActionCard({
const designSettings = (
<>
- {/* Color picker */}
-
{/* Layout settings */}
-
+ {/* Color picker */}
+
{/* Sponsor label setting */}
{/* Image setting */}
-
+
{/* Button settings */}
{showButton && (
<>
-
@@ -264,6 +286,7 @@ export function CallToActionCard({
layout === 'immersive' ? 'h-auto w-full' : 'aspect-square w-16 object-cover',
'rounded-md'
)}
+ data-testid="cta-card-image"
src={imageSrc}
/>
@@ -280,7 +303,7 @@ export function CallToActionCard({
placeholderClassName={`bg-transparent whitespace-normal font-serif text-xl !text-grey-500 !dark:text-grey-800 ` }
placeholderText="Write something worth clicking..."
textClassName={clsx(
- 'w-full whitespace-normal text-pretty bg-transparent font-serif text-xl text-grey-900 dark:text-grey-200',
+ 'koenig-lexical-cta-text w-full whitespace-normal text-pretty bg-transparent font-serif text-xl text-grey-900 dark:text-grey-200',
layout === 'immersive' ? 'text-center' : 'text-left'
)}
>
@@ -295,8 +318,8 @@ export function CallToActionCard({
dataTestId="cta-button"
placeholder="Add button text"
size={layout === 'immersive' ? 'medium' : 'small'}
- style={buttonColor ? {
- backgroundColor: buttonColor === 'accent' ? 'var(--accent-color)' : buttonColor,
+ style={buttonColor !== 'accent' ? {
+ backgroundColor: buttonColor,
color: buttonTextColor
} : undefined}
value={buttonText}
@@ -332,7 +355,7 @@ CallToActionCard.propTypes = {
buttonUrl: PropTypes.string,
buttonColor: PropTypes.string,
buttonTextColor: PropTypes.string,
- color: PropTypes.oneOf(['none', 'grey', 'white', 'blue', 'green', 'yellow', 'red']),
+ color: PropTypes.oneOf(['none', 'grey', 'white', 'blue', 'green', 'yellow', 'red', 'pink', 'purple']),
hasSponsorLabel: PropTypes.bool,
imageSrc: PropTypes.string,
isEditing: PropTypes.bool,
@@ -352,6 +375,8 @@ CallToActionCard.propTypes = {
onRemoveMedia: PropTypes.func,
sponsorLabelHtmlEditor: PropTypes.object,
sponsorLabelHtmlEditorInitialState: PropTypes.object,
- visibilityOptions: PropTypes.object,
- toggleVisibility: PropTypes.func
+ visibilityOptions: PropTypes.array,
+ toggleVisibility: PropTypes.func,
+ imageUploadHandler: PropTypes.func,
+ imageDragHandler: PropTypes.object
};
diff --git a/packages/koenig-lexical/src/components/ui/cards/CalloutCard.jsx b/packages/koenig-lexical/src/components/ui/cards/CalloutCard.jsx
index 654e22c839..f40bb1c50f 100644
--- a/packages/koenig-lexical/src/components/ui/cards/CalloutCard.jsx
+++ b/packages/koenig-lexical/src/components/ui/cards/CalloutCard.jsx
@@ -43,37 +43,37 @@ export const calloutColorPicker = [
{
label: 'Grey',
name: 'grey',
- color: 'bg-grey/15 border-black/[0.08] dark:border-white/10'
+ color: 'bg-grey/20 border-black/[0.08] dark:border-white/10'
},
{
label: 'Blue',
name: 'blue',
- color: 'bg-blue/15 border-black/[0.08] dark:border-white/10'
+ color: 'bg-blue/20 border-black/[0.08] dark:border-white/10'
},
{
label: 'Green',
name: 'green',
- color: 'bg-green/15 border-black/[0.08] dark:border-white/10'
+ color: 'bg-green/20 border-black/[0.08] dark:border-white/10'
},
{
label: 'Yellow',
name: 'yellow',
- color: 'bg-yellow/15 border-black/[0.08] dark:border-white/10'
+ color: 'bg-yellow/20 border-black/[0.08] dark:border-white/10'
},
{
label: 'Red',
name: 'red',
- color: 'bg-red/15 border-black/[0.08] dark:border-white/10'
+ color: 'bg-red/20 border-black/[0.08] dark:border-white/10'
},
{
label: 'Pink',
name: 'pink',
- color: 'bg-pink/15 border-black/[0.08] dark:border-white/10'
+ color: 'bg-pink/20 border-black/[0.08] dark:border-white/10'
},
{
label: 'Purple',
name: 'purple',
- color: 'bg-purple/15 border-black/[0.08] dark:border-white/10'
+ color: 'bg-purple/20 border-black/[0.08] dark:border-white/10'
},
{
label: 'Accent',
diff --git a/packages/koenig-lexical/src/components/ui/cards/HeaderCard/v1/HeaderCard.jsx b/packages/koenig-lexical/src/components/ui/cards/HeaderCard/v1/HeaderCard.jsx
index fae3396026..0b6c7da56f 100644
--- a/packages/koenig-lexical/src/components/ui/cards/HeaderCard/v1/HeaderCard.jsx
+++ b/packages/koenig-lexical/src/components/ui/cards/HeaderCard/v1/HeaderCard.jsx
@@ -178,6 +178,7 @@ export function HeaderCard({isEditing,
/>
{
+ if (!enabled) {
+ return;
+ }
+
+ const handleClickOutside = (event) => {
+ if (ref.current && !ref.current.contains(event.target)) {
+ handler();
+ }
+ };
+
+ window.addEventListener('mousedown', handleClickOutside, {capture: true});
+ return () => window.removeEventListener('mousedown', handleClickOutside, {capture: true});
+ }, [enabled, handler, ref]);
+}
diff --git a/packages/koenig-lexical/src/hooks/useMovable.js b/packages/koenig-lexical/src/hooks/useMovable.js
index 720246ebfe..b13149b592 100644
--- a/packages/koenig-lexical/src/hooks/useMovable.js
+++ b/packages/koenig-lexical/src/hooks/useMovable.js
@@ -125,12 +125,16 @@ export default function useMovable({adjustOnResize, adjustOnDrag} = {}) {
// preventing clicks stops any event handlers that may otherwise result in the
// movable element being closed when the drag finishes
const disablePointerEvents = useCallback(() => {
- ref.current.style.pointerEvents = 'none';
+ if (ref.current) {
+ ref.current.style.pointerEvents = 'none';
+ }
window.addEventListener('click', cancelClick, {capture: true, passive: false});
}, [ref, cancelClick]);
const enablePointerEvents = useCallback(() => {
- ref.current.style.pointerEvents = '';
+ if (ref.current) {
+ ref.current.style.pointerEvents = '';
+ }
window.removeEventListener('click', cancelClick, {capture: true, passive: false});
}, [ref, cancelClick]);
diff --git a/packages/koenig-lexical/src/nodes/CallToActionNodeComponent.jsx b/packages/koenig-lexical/src/nodes/CallToActionNodeComponent.jsx
index 7b6050b199..1dfb890985 100644
--- a/packages/koenig-lexical/src/nodes/CallToActionNodeComponent.jsx
+++ b/packages/koenig-lexical/src/nodes/CallToActionNodeComponent.jsx
@@ -1,6 +1,7 @@
import CardContext from '../context/CardContext';
import KoenigComposerContext from '../context/KoenigComposerContext.jsx';
import React, {useRef} from 'react';
+import useFileDragAndDrop from '../hooks/useFileDragAndDrop';
import {$getNodeByKey} from 'lexical';
import {ActionToolbar} from '../components/ui/ActionToolbar.jsx';
import {CallToActionCard} from '../components/ui/cards/CallToActionCard.jsx';
@@ -24,7 +25,6 @@ export const CallToActionNodeComponent = ({
htmlEditor,
htmlEditorInitialState,
buttonTextColor,
- href,
sponsorLabelHtmlEditor,
sponsorLabelHtmlEditorInitialState
}) => {
@@ -32,6 +32,7 @@ export const CallToActionNodeComponent = ({
const {isEditing, isSelected, setEditing} = React.useContext(CardContext);
const {fileUploader, cardConfig} = React.useContext(KoenigComposerContext);
const [showSnippetToolbar, setShowSnippetToolbar] = React.useState(false);
+ const imageDragHandler = useFileDragAndDrop({handleDrop: handleImageDrop});
const {visibilityOptions, toggleVisibility} = useVisibilityToggle(editor, nodeKey, cardConfig);
@@ -124,6 +125,10 @@ export const CallToActionNodeComponent = ({
});
};
+ async function handleImageDrop(files) {
+ await handleImageChange(files);
+ }
+
React.useEffect(() => {
htmlEditor.setEditable(isEditing);
}, [isEditing, htmlEditor]);
@@ -141,6 +146,7 @@ export const CallToActionNodeComponent = ({
hasSponsorLabel={hasSponsorLabel}
htmlEditor={htmlEditor}
htmlEditorInitialState={htmlEditorInitialState}
+ imageDragHandler={imageDragHandler}
imageSrc={imageUrl}
imageUploader={imageUploader}
isEditing={isEditing}
diff --git a/packages/koenig-lexical/src/styles/components/kg-prose.css b/packages/koenig-lexical/src/styles/components/kg-prose.css
index d183aad1d3..b7f945150c 100644
--- a/packages/koenig-lexical/src/styles/components/kg-prose.css
+++ b/packages/koenig-lexical/src/styles/components/kg-prose.css
@@ -981,6 +981,14 @@
}
}
+.koenig-lexical-cta-label a {
+ @apply !text-grey-900 dark:!text-grey-200 underline;
+}
+
+.koenig-lexical-cta-text a {
+ @apply !text-grey-900 dark:!text-grey-200;
+}
+
/* stylelint-enable at-rule-disallowed-list */
diff --git a/packages/koenig-lexical/tailwind.config.cjs b/packages/koenig-lexical/tailwind.config.cjs
index f6133fb4fe..925d003c2e 100644
--- a/packages/koenig-lexical/tailwind.config.cjs
+++ b/packages/koenig-lexical/tailwind.config.cjs
@@ -91,6 +91,7 @@ module.exports = {
},
boxShadow: {
DEFAULT: '0 0 1px rgba(0,0,0,.15), 0px 13px 27px -5px rgba(50, 50, 93, 0.08), 0px 8px 16px -8px rgba(0, 0, 0, 0.12)',
+ xs: '0px 1px 2px rgba(0, 0, 0, 0.06)',
sm: '0px 2px 5px -1px rgba(50, 50, 93, 0.2), 0px 1px 3px -1px rgba(0, 0, 0, 0.25)',
md: '0px 13px 27px -5px rgba(50, 50, 93, 0.25), 0px 8px 16px -8px rgba(0, 0, 0, 0.3)',
lg: '0px 50px 100px -25px rgba(50, 50, 93, 0.2), 0px 30px 60px -20px rgba(0, 0, 0, 0.25)',
diff --git a/packages/koenig-lexical/test/e2e/cards/cta-card.test.js b/packages/koenig-lexical/test/e2e/cards/call-to-action-card.test.js
similarity index 87%
rename from packages/koenig-lexical/test/e2e/cards/cta-card.test.js
rename to packages/koenig-lexical/test/e2e/cards/call-to-action-card.test.js
index 26e05e34a0..a3da9151c0 100644
--- a/packages/koenig-lexical/test/e2e/cards/cta-card.test.js
+++ b/packages/koenig-lexical/test/e2e/cards/call-to-action-card.test.js
@@ -1,5 +1,6 @@
import path from 'path';
-import {assertHTML, focusEditor, getEditorStateJSON, html, initialize, insertCard} from '../../utils/e2e';
+import {assertHTML, createDataTransfer, focusEditor, getEditorStateJSON, html, initialize, insertCard} from '../../utils/e2e';
+import {cardBackgroundColorSettings} from '../../utils/background-color-helper';
import {expect, test} from '@playwright/test';
import {fileURLToPath} from 'url';
const __filename = fileURLToPath(import.meta.url);
@@ -118,37 +119,30 @@ test.describe('Call To Action Card', async () => {
`, {ignoreCardContents: true});
});
- test('can toggle button on card', async function () {
+ test('button and button settings is visible by default', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
expect(await page.isVisible('[data-testid="cta-button"]')).toBe(true);
-
- await page.click('[data-testid="button-settings"]');
-
- expect(await page.isVisible('[data-testid="cta-button"]')).toBe(false);
+ expect(await page.isVisible('[data-testid="cta-button-color"]')).toBe(true);
+ expect(await page.isVisible('[data-testid="button-text"]')).toBe(true);
+ expect(await page.isVisible('[data-testid="button-url"]')).toBe(true);
});
- test('button settings expands and collapses when toggled', async function () {
+ test('can toggle button on card and expands settings', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
+ expect(await page.isVisible('[data-testid="cta-button"]')).toBe(true);
await page.click('[data-testid="button-settings"]');
- // determine if settings are open byy looking for cta-button-color, button-text & button-url
- expect(await page.isVisible('[data-testid="cta-button-color"]')).toBe(true);
- expect(await page.isVisible('[data-testid="button-text"]')).toBe(true);
- expect(await page.isVisible('[data-testid="button-url"]')).toBe(true);
+ expect(await page.isVisible('[data-testid="cta-button"]')).toBe(false);
await page.click('[data-testid="button-settings"]');
- // determine if settings are closed by looking for cta-button-color, button-text & button-url
- expect(await page.isVisible('[data-testid="cta-button-color"]')).toBe(false);
- expect(await page.isVisible('[data-testid="button-text"]')).toBe(false);
- expect(await page.isVisible('[data-testid="button-url"]')).toBe(false);
+
+ expect(await page.isVisible('[data-testid="cta-button"]')).toBe(true);
});
test('can set button text', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
await page.fill('[data-testid="button-text"]', 'Click me');
expect(await page.textContent('[data-testid="cta-button"]')).toBe('Click me');
});
@@ -156,7 +150,6 @@ test.describe('Call To Action Card', async () => {
test('can set button url', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
await page.fill('[data-testid="button-url"]', 'https://example.com/somepost');
const buttonContainer = await page.$('[data-test-cta-button-current-url]');
const currentUrl = await buttonContainer.getAttribute('data-test-cta-button-current-url');
@@ -167,7 +160,6 @@ test.describe('Call To Action Card', async () => {
test('suggested urls display', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
const buttonTextInput = await page.getByTestId('button-url');
await expect(buttonTextInput).toHaveValue('');
@@ -188,7 +180,6 @@ test.describe('Call To Action Card', async () => {
test('button doesnt disappear when toggled, has text, has url and loses focus', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
await page.fill('[data-testid="button-text"]', 'Click me');
await page.fill('[data-testid="button-url"]', 'https://example.com/somepost');
expect(await page.isVisible('[data-testid="cta-button"]')).toBe(true);
@@ -217,16 +208,15 @@ test.describe('Call To Action Card', async () => {
test('default button colour is accent', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
expect(await page.getAttribute('[data-testid="cta-button"]', 'class')).toContain('bg-accent');
});
test('can change button colour to black', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
// find the parent element cta-button-color and select child button with title=black
- await page.click('[data-testid="cta-button-color"] button[title="Black"]');
+ // await page.click('[data-testid="cta-button-color"] button[title="Black"]');
+ await cardBackgroundColorSettings(page, {cardColorPickerTestId: 'cta-button-color', findByColorTitle: 'Black'});
// check if the button has style="background-color: rgb(0, 0, 0);"
expect(await page.getAttribute('[data-testid="cta-button"]', 'style')).toContain('background-color: rgb(0, 0, 0);');
});
@@ -234,9 +224,8 @@ test.describe('Call To Action Card', async () => {
test('can change button colour to grey', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
// find the parent element cta-button-color and select child button with title=white
- await page.click('[data-testid="cta-button-color"] button[title="Grey"]');
+ await cardBackgroundColorSettings(page, {cardColorPickerTestId: 'cta-button-color', findByColorTitle: 'Grey'});
// check if the button has style="background-color: rgb(255, 255, 255);"
expect(await page.getAttribute('[data-testid="cta-button"]', 'style')).toContain('background-color: rgb(240, 240, 240);');
});
@@ -244,19 +233,16 @@ test.describe('Call To Action Card', async () => {
test('can use colour picker to change button colour', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
- await page.click('button[aria-label="Pick color"]');
- await page.fill('input[aria-label="Color value"]', 'ff0000');
+ await cardBackgroundColorSettings(page, {cardColorPickerTestId: 'cta-button-color', customColor: 'ff0000'});
expect(await page.getAttribute('[data-testid="cta-button"]', 'style')).toContain('background-color: rgb(255, 0, 0);');
});
test('button text colour changes with button colour', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
- await page.click('[data-testid="button-settings"]');
await page.fill('[data-testid="button-text"]', 'Click me');
- await page.click('button[aria-label="Pick color"]');
- await page.fill('input[aria-label="Color value"]', 'FFFFFF');
+
+ await cardBackgroundColorSettings(page, {cardColorPickerTestId: 'cta-button-color', customColor: 'FFFFFF'});
expect(await page.getAttribute('[data-testid="cta-button"]', 'style')).toContain('color: rgb(255, 255, 255);');
// change button colour to black
@@ -326,19 +312,38 @@ test.describe('Call To Action Card', async () => {
{testId: 'color-picker-green', expectedClass: 'bg-green'},
{testId: 'color-picker-blue', expectedClass: 'bg-blue'},
{testId: 'color-picker-yellow', expectedClass: 'bg-yellow'},
- {testId: 'color-picker-red', expectedClass: 'bg-red'}
+ {testId: 'color-picker-red', expectedClass: 'bg-red'},
+ {testId: 'color-picker-pink', expectedClass: 'bg-pink'},
+ {testId: 'color-picker-purple', expectedClass: 'bg-purple'}
];
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
const firstChildSelector = '[data-kg-card="call-to-action"] > :first-child';
- await expect(page.locator(firstChildSelector)).not.toHaveClass(/bg-(green|blue|yellow|red)/); // shouldn't have any of the classes yet
+ await expect(page.locator(firstChildSelector)).not.toHaveClass(/bg-(green|blue|yellow|red|pink|purple)/); // shouldn't have any of the classes yet
for (const color of colors) {
- await page.click(`[data-test-id="${color.testId}"]`);
+ await page.locator('[data-testid="cta-background-color-picker"] button').click();
+ await page.locator(`[data-test-id="${color.testId}"]`).click();
await expect(page.locator(firstChildSelector)).toHaveClass(new RegExp(color.expectedClass));
}
});
+ test('background color popup closes on outside click', async function () {
+ await focusEditor(page);
+ await insertCard(page, {cardName: 'call-to-action'});
+
+ const colorOptions = page.getByTestId('cta-background-color-picker');
+ await colorOptions.getByTestId('color-options-button').click();
+
+ await expect(colorOptions.getByTestId('color-options-popover')).toBeVisible();
+
+ const card = page.locator('[data-kg-card="call-to-action"]');
+ const settings = card.getByTestId('settings-panel');
+ await settings.getByTestId('media-upload-setting').click();
+
+ await expect(colorOptions.getByTestId('color-options-popover')).not.toBeVisible();
+ });
+
test('can add and remove CTA Card image', async function () {
const filePath = path.relative(process.cwd(), __dirname + `/../fixtures/large-image.jpeg`);
@@ -359,6 +364,23 @@ test.describe('Call To Action Card', async () => {
await expect(imgLocator).not.toBeVisible();
});
+ test('can drag and drop image over upload button', async function () {
+ const filePath = path.relative(process.cwd(), __dirname + '/../fixtures/large-image.png');
+ await focusEditor(page);
+ await insertCard(page, {cardName: 'call-to-action'});
+
+ // Create and dispatch data transfer
+ const dataTransfer = await createDataTransfer(page, [{filePath, fileName: 'large-image.png', fileType: 'image/png'}]);
+ await page.getByTestId('media-upload-placeholder').dispatchEvent('dragover', {dataTransfer});
+ // Dragover text should be visible
+ // check that "Drop it like it's hot" is visible
+ await expect(await page.locator('[data-kg-card-drag-text="true"]')).toBeVisible();
+
+ await page.getByTestId('media-upload-placeholder').dispatchEvent('drop', {dataTransfer});
+
+ await expect (await page.getByTestId('cta-card-image')).toBeVisible();
+ });
+
test('default layout is minimal', async function () {
await focusEditor(page);
await insertCard(page, {cardName: 'call-to-action'});
diff --git a/packages/koenig-lexical/test/e2e/cards/gallery-card.test.js b/packages/koenig-lexical/test/e2e/cards/gallery-card.test.js
index 50d610f135..9c14befb78 100644
--- a/packages/koenig-lexical/test/e2e/cards/gallery-card.test.js
+++ b/packages/koenig-lexical/test/e2e/cards/gallery-card.test.js
@@ -507,7 +507,8 @@ test.describe('Gallery card', async () => {
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(filePaths);
- await expect(page.locator('[data-testid="gallery-image"]')).toHaveCount(9);
+ await expect(page.getByTestId('progress-bar')).not.toBeVisible();
+ await expect(page.getByTestId('gallery-image')).toHaveCount(9);
});
const editorState = await getEditorState(page);
diff --git a/packages/koenig-lexical/test/utils/background-color-helper.js b/packages/koenig-lexical/test/utils/background-color-helper.js
new file mode 100644
index 0000000000..9f7cd0b5d7
--- /dev/null
+++ b/packages/koenig-lexical/test/utils/background-color-helper.js
@@ -0,0 +1,28 @@
+export async function cardBackgroundColorSettings(page, {cardColorPickerTestId, customColor, colorTestId, findByColorTitle, imageUploadId, fireColorSetting = true}) {
+ if (fireColorSetting) {
+ const colorSetting = page.locator(`[data-testid="${cardColorPickerTestId}"]`);
+ const colorButton = colorSetting.locator('button');
+ await colorButton.click();
+ }
+
+ if (findByColorTitle) {
+ const colorTitle = page.locator(`[title="${findByColorTitle}"]`);
+ await colorTitle.click();
+ }
+
+ if (customColor) {
+ const picker = page.locator(`[data-testid="color-picker-toggle"]`);
+ await picker.click();
+ const colorInput = page.locator(`input[aria-label="Color value"]`);
+ await colorInput.click({clickCount: 3});
+ await colorInput.type(customColor);
+ }
+
+ if (colorTestId) {
+ await page.locator(`[data-test-id="${colorTestId}"]`).click();
+ }
+
+ if (imageUploadId) {
+ await page.locator(`[data-testid="${imageUploadId}"]`).click();
+ }
+}