Skip to content
Snippets Groups Projects
Commit 0735fd7f authored by utkarsh_33's avatar utkarsh_33 Committed by Jesse Baker
Browse files

Issue #3464814 by Utkarsh_33, Wim Leers, hooroomoo, syeda-farheen: Fix undo/redo functionality

parent ae32ea46
No related branches found
No related tags found
1 merge request!132#3464814: Fixed undo/redo
Pipeline #247648 passed
describe('Undo/Redo functionality', { testIsolation: false }, () => {
before(() => {
cy.drupalXbInstall();
});
after(() => {
cy.drupalUninstall();
});
beforeEach(() => {
cy.drupalSession();
cy.viewport(2000, 1000);
cy.drupalLogin('xbUser', 'xbUser');
});
it('Performs a basic interaction with Undo/Redo', () => {
cy.drupalRelativeURL('xb/node/1');
// Wait for the preview iframe to load and render something that confirms it is ready.
cy.get('iframe[data-xb-preview]').should('exist');
cy.waitForElementInIframe('[data-xb-type="experience_builder:image"]');
// Assert that the undo button is disabled initially.
cy.get('button[aria-label="Undo"]').should('be.disabled');
// Check there are three heroes initially.
cy.testInIframe('[data-component-id="experience_builder:my-hero"]',
(myHeroComponent) => {
expect(myHeroComponent.length).to.equal(3);
});
// Check that the menu is not open yet.
cy.get('[data-radix-menubar-content]').should('have.length', 0);
cy.scrollToMiddleOfIframe();
cy.getIframeBody().
find('[data-component-id="experience_builder:my-hero"] h1').
first().
trigger('click');
cy.get('button[aria-label="Add section"]').then((button) => {
button.click();
});
// Confirm that the menu opens the Section templates.
cy.get('[data-radix-menubar-content]').should('have.length', 2);
cy.get('[data-radix-menu-content].MenubarSubContent').
should('contain.text', 'Section templates placeholder');
cy.intercept('POST', '**/api/preview/node/1').as('getPreview');
// Click on the menu item with data-xb-name="Hero" inside menu.
cy.get('[data-radix-menu-content] [data-xb-name="Hero"]').
click().
then(() => {
cy.wait('@getPreview');
});
cy.getIframeBody().find('[data-component-id="experience_builder:my-hero"]',
(myHeroComponent) => {
expect(myHeroComponent.length).to.equal(4)
});
cy.get('button[aria-label="Undo"]').
click().
then(() => {
cy.wait('@getPreview');
});
// Assert that the component was deleted from the layout.
cy.getIframeBody().find('[data-component-id="experience_builder:my-hero"]',
(myHeroComponent) => {
expect(myHeroComponent.length).to.equal(3)
});
// Click the Redo button.
cy.get('button[aria-label="Redo"]').
click().
then(() => {
cy.wait('@getPreview');
});
// Assert that the component was again added to the layout.
cy.getIframeBody().find('[data-component-id="experience_builder:my-hero"]',
(myHeroComponent) => {
expect(myHeroComponent.length).to.equal(4)
});
});
});
......@@ -11,7 +11,7 @@ import { useEffect } from 'react';
const UndoRedo = () => {
const dispatch = useAppDispatch();
const layoutModel = useAppSelector(selectHistory);
const isUndoable = layoutModel.past.length > 0;
const isUndoable = layoutModel.past.length > 1;
const isRedoable = layoutModel.future.length > 0;
const dispatchUndo = () =>
isUndoable ? dispatch(ActionCreators.undo()) : null;
......@@ -46,6 +46,7 @@ const UndoRedo = () => {
highContrast
onClick={() => dispatchUndo()}
disabled={!isUndoable}
aria-label="Undo"
>
<ResetIcon /> Undo
</Button>
......@@ -55,6 +56,7 @@ const UndoRedo = () => {
highContrast
onClick={() => dispatchRedo()}
disabled={!isRedoable}
aria-label="Redo"
>
<ResetIcon className={styles.topBarRedoIcon} /> Redo
</Button>
......
......@@ -64,6 +64,7 @@ type DuplicateNodePayload = {
type InsertNodePayload = {
newNode: LayoutNode | undefined;
to: number[] | undefined;
model: InitialPropData | undefined;
};
type AddNewNodePayload = {
......@@ -86,10 +87,6 @@ type InitialPropData = {
[key: string]: any;
};
type CreateModelPayload = {
uuid: string | undefined;
initialData: InitialPropData | undefined;
};
export interface ComponentModel {
[key: string]: string | boolean | [] | number;
name: string;
......@@ -145,7 +142,7 @@ export const layoutModelSlice = createSlice({
),
insertNode: create.reducer(
(state, action: PayloadAction<InsertNodePayload>) => {
const { newNode, to } = action.payload;
const { newNode, to, model } = action.payload;
if (!newNode || !Array.isArray(to)) {
console.error(
`Cannot move ${newNode} to position ${to}. Check both uuid and to are defined/valid.`,
......@@ -154,6 +151,7 @@ export const layoutModelSlice = createSlice({
}
state.layout = insertNodeAtPath(state.layout, to, newNode);
state.model[newNode.uuid] = { ...state.model[newNode.uuid], ...model };
},
),
sortNode: create.reducer(
......@@ -217,14 +215,6 @@ export const layoutModelSlice = createSlice({
}
},
),
createNewModel: create.reducer(
(state, action: PayloadAction<CreateModelPayload>) => {
const { uuid, initialData } = action.payload;
if (uuid) {
state.model[uuid] = { ...state.model[uuid], ...initialData };
}
},
),
}),
});
......@@ -232,17 +222,6 @@ export const addNewComponentToLayout =
(payload: AddNewNodePayload) => (dispatch: AppDispatch) => {
if (payload.newNode && payload.to) {
const uuid = uuidv4();
dispatch(
insertNode({
to: payload.to,
newNode: {
uuid: uuid,
children: [],
nodeType: 'component',
type: payload.newNode,
},
}),
);
const initialData: InitialPropData = {};
if (payload?.componentFieldData) {
// @todo Update this logic in https://www.drupal.org/project/experience_builder/issues/3455942
......@@ -254,9 +233,15 @@ export const addNewComponentToLayout =
});
}
dispatch(
createNewModel({
uuid: uuid,
initialData,
insertNode({
to: payload.to,
newNode: {
uuid: uuid,
children: [],
nodeType: 'component',
type: payload.newNode,
},
model: initialData,
}),
);
}
......@@ -272,7 +257,6 @@ export const {
sortNode,
insertNode,
updateNodeModel,
createNewModel,
} = layoutModelSlice.actions;
export const layoutModelReducer = layoutModelSlice.reducer;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment