Skip to content

Commit 94922b3

Browse files
committed
Merge branch 'refs/heads/dev' into stable
2 parents e3f273e + 6e1f5ac commit 94922b3

File tree

11 files changed

+187
-82
lines changed

11 files changed

+187
-82
lines changed

packages/api-form-builder/__tests__/graphql/i18n.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,19 @@ export const CREATE_LOCALE = /* GraphQL */ `
1313
}
1414
}
1515
`;
16+
17+
export const DELETE_LOCALE = /* GraphQL */ `
18+
mutation DeleteI18NLocale($code: String!) {
19+
i18n {
20+
deleteI18NLocale(code: $code) {
21+
data {
22+
code
23+
}
24+
error {
25+
message
26+
code
27+
}
28+
}
29+
}
30+
}
31+
`;

packages/api-form-builder/__tests__/settings.test.ts

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@ import useGqlHandler from "./useGqlHandler";
22
import { GET_SETTINGS } from "~tests/graphql/formBuilderSettings";
33

44
describe("Settings Test", () => {
5-
const { getSettings, updateSettings, install, createI18NLocale, isInstalled } = useGqlHandler();
6-
7-
test(`Should not be able to get & update settings before "install"`, async () => {
5+
const {
6+
getSettings,
7+
updateSettings,
8+
install,
9+
createI18NLocale,
10+
deleteI18NLocale,
11+
isInstalled
12+
} = useGqlHandler();
13+
14+
it(`Should not be able to get & update settings before "install"`, async () => {
815
// Should not have any settings without install
916
const [getSettingsResponse] = await getSettings();
1017

@@ -40,7 +47,7 @@ describe("Settings Test", () => {
4047
});
4148
});
4249

43-
test("Should be able to install `Form Builder`", async () => {
50+
it("Should be able to install `Form Builder`", async () => {
4451
// "isInstalled" should return false prior "install"
4552
const [isInstalledResponse] = await isInstalled();
4653

@@ -78,7 +85,7 @@ describe("Settings Test", () => {
7885
});
7986
});
8087

81-
test(`Should be able to get & update settings after "install"`, async () => {
88+
it(`Should be able to get & update settings after "install"`, async () => {
8289
// Let's install the `Form builder`
8390
const [installResponse] = await install({ domain: "http://localhost:3001" });
8491

@@ -156,7 +163,7 @@ describe("Settings Test", () => {
156163
});
157164
});
158165

159-
test(`Should be able to get & update settings after in a new locale`, async () => {
166+
it(`Should be able to get & update settings after in a new locale`, async () => {
160167
// Let's install the `Form builder`
161168
await install({ domain: "http://localhost:3001" });
162169

@@ -168,9 +175,7 @@ describe("Settings Test", () => {
168175
// set the locale header. Wasn't easily possible via the `getSettings` helper.
169176
const [newLocaleFbSettings] = await invoke({
170177
body: { query: GET_SETTINGS },
171-
headers: {
172-
"x-i18n-locale": "default:de-DE;content:de-DE;"
173-
}
178+
headers: { "x-i18n-locale": "default:de-DE;content:de-DE;" }
174179
});
175180

176181
// Settings should exist in the newly created locale.
@@ -192,4 +197,38 @@ describe("Settings Test", () => {
192197
}
193198
});
194199
});
200+
201+
it(`Should be able to create a locale, delete it, and again create it`, async () => {
202+
// Let's install the `Form builder`
203+
await install({ domain: "http://localhost:3001" });
204+
205+
await createI18NLocale({ data: { code: "en-US" } });
206+
await createI18NLocale({ data: { code: "de-DE" } });
207+
208+
const [deleteDeLocaleResponse] = await deleteI18NLocale({ code: "de-DE" });
209+
expect(deleteDeLocaleResponse).toEqual({
210+
data: {
211+
i18n: {
212+
deleteI18NLocale: {
213+
data: { code: "de-DE" },
214+
error: null
215+
}
216+
}
217+
}
218+
});
219+
220+
const [createDeLocaleResponse] = await createI18NLocale({ data: { code: "de-DE" } });
221+
expect(createDeLocaleResponse).toEqual({
222+
data: {
223+
i18n: {
224+
createI18NLocale: {
225+
data: {
226+
code: "de-DE"
227+
},
228+
error: null
229+
}
230+
}
231+
}
232+
});
233+
});
195234
});

packages/api-form-builder/__tests__/useGqlHandler.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { createI18NGraphQL } from "@webiny/api-i18n/graphql";
1212

1313
// Graphql
1414
import { INSTALL as INSTALL_FILE_MANAGER } from "./graphql/fileManagerSettings";
15-
import { CREATE_LOCALE } from "./graphql/i18n";
15+
import { DELETE_LOCALE, CREATE_LOCALE } from "./graphql/i18n";
1616

1717
import {
1818
GET_SETTINGS,
@@ -228,6 +228,9 @@ export default (params: UseGqlHandlerParams = {}) => {
228228
// Locales.
229229
async createI18NLocale(variables: Record<string, any>) {
230230
return invoke({ body: { query: CREATE_LOCALE, variables } });
231+
},
232+
async deleteI18NLocale(variables: Record<string, any>) {
233+
return invoke({ body: { query: DELETE_LOCALE, variables } });
231234
}
232235
};
233236
};

packages/api-form-builder/src/plugins/crud/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ export default (params: CreateFormBuilderCrudParams) => {
122122
return context.formBuilder.createSettings({});
123123
});
124124
});
125+
126+
context.i18n.locales.onLocaleAfterDelete.subscribe(async params => {
127+
const { locale } = params;
128+
await context.i18n.withLocale(locale, async () => {
129+
return context.formBuilder.deleteSettings();
130+
});
131+
});
125132
})
126133
];
127134
};

packages/app-headless-cms/src/admin/components/ContentEntryForm/useBind.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,11 @@ export function useBind({ Bind, field }: UseBindProps) {
101101
if (index < 0) {
102102
return;
103103
}
104-
let value = bind.value;
105-
value = [...value.slice(0, index), ...value.slice(index + 1)];
104+
105+
const value = [
106+
...bind.value.slice(0, index),
107+
...bind.value.slice(index + 1)
108+
];
106109

107110
bind.onChange(value.length === 0 ? null : value);
108111

packages/app-headless-cms/src/admin/plugins/fieldRenderers/object/multipleObjects.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,12 @@ const Actions = ({ setHighlightIndex, bind, index }: ActionsProps) => {
6868
[moveValueUp, index]
6969
);
7070

71-
const onDelete = useCallback(
72-
(ev: React.BaseSyntheticEvent) => {
73-
ev.stopPropagation();
74-
showConfirmation(() => {
75-
bind.field.removeValue(index);
76-
});
77-
},
78-
[index]
79-
);
71+
const onDelete = (ev: React.BaseSyntheticEvent) => {
72+
ev.stopPropagation();
73+
showConfirmation(() => {
74+
bind.field.removeValue(index);
75+
});
76+
};
8077

8178
return (
8279
<>

packages/app-headless-cms/src/admin/plugins/fieldRenderers/object/multipleObjectsAccordion.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,12 @@ const Actions = ({ setHighlightIndex, bind, index }: ActionsProps) => {
7070
[moveValueUp, index]
7171
);
7272

73-
const onDelete = useCallback(
74-
(ev: React.BaseSyntheticEvent) => {
75-
ev.stopPropagation();
76-
showConfirmation(() => {
77-
bind.field.removeValue(index);
78-
});
79-
},
80-
[index]
81-
);
73+
const onDelete = (ev: React.BaseSyntheticEvent) => {
74+
ev.stopPropagation();
75+
showConfirmation(() => {
76+
bind.field.removeValue(index);
77+
});
78+
};
8279

8380
return (
8481
<>

packages/app-page-builder/src/editor/hooks/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ export { useRootElement } from "./useRootElement";
1717
export { useUI } from "./useUI";
1818
export { useUpdateElement } from "./useUpdateElement";
1919
export { useUpdateHandlers } from "./useUpdateHandlers";
20+
export { useDeleteElement } from "./useDeleteElement";
21+
export { useFindElementBlock } from "./useFindElementBlock";
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { useCallback } from "react";
2+
import { plugins } from "@webiny/plugins";
3+
import { useEventActionHandler, useFindElementBlock, useUpdateElement } from "~/editor";
4+
import { DeleteElementActionEvent } from "~/editor/recoil/actions";
5+
import type { PbBlockVariable, PbEditorElement, PbEditorPageElementPlugin } from "~/types";
6+
7+
const removeVariableFromBlock = (block: PbEditorElement, variableId: string) => {
8+
const variables = block.data.variables ?? [];
9+
10+
const updatedVariables = variables.filter(
11+
(variable: PbBlockVariable) => variable.id.split(".")[0] !== variableId
12+
);
13+
14+
return {
15+
...block,
16+
data: {
17+
...block.data,
18+
variables: updatedVariables
19+
}
20+
};
21+
};
22+
23+
export const useDeleteElement = () => {
24+
const eventActionHandler = useEventActionHandler();
25+
const updateElement = useUpdateElement();
26+
const { findElementBlock } = useFindElementBlock();
27+
28+
const canDeleteElement = useCallback((element: PbEditorElement) => {
29+
const plugin = plugins
30+
.byType<PbEditorPageElementPlugin>("pb-editor-page-element")
31+
.find(pl => pl.elementType === element.type);
32+
33+
if (!plugin) {
34+
return false;
35+
}
36+
37+
if (typeof plugin.canDelete === "function") {
38+
if (!plugin.canDelete({ element })) {
39+
return false;
40+
}
41+
}
42+
43+
return true;
44+
}, []);
45+
46+
const deleteElement = useCallback(async (element: PbEditorElement): Promise<void> => {
47+
const block = await findElementBlock(element.id);
48+
49+
// We need to remove element variable from block if it exists
50+
if (element.data?.variableId && block) {
51+
const updatedBlock = removeVariableFromBlock(block, element.data.variableId);
52+
53+
updateElement(updatedBlock);
54+
}
55+
56+
eventActionHandler.trigger(
57+
new DeleteElementActionEvent({
58+
element
59+
})
60+
);
61+
}, []);
62+
63+
return { canDeleteElement, deleteElement };
64+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { useCallback } from "react";
2+
import { useRecoilCallback } from "recoil";
3+
import { blockByElementSelector } from "~/editor/hooks/useCurrentBlockElement";
4+
5+
/**
6+
* Exposes a getter which traverses the element tree upwards from the given element id, and returns an element
7+
* of type "block", if found.
8+
*/
9+
export const useFindElementBlock = () => {
10+
const findBlock = useRecoilCallback(({ snapshot }) => async (id: string) => {
11+
return await snapshot.getPromise(blockByElementSelector(id));
12+
});
13+
14+
const findElementBlock = useCallback(
15+
async (elementId: string) => {
16+
return findBlock(elementId);
17+
},
18+
[findBlock]
19+
);
20+
21+
return { findElementBlock };
22+
};

0 commit comments

Comments
 (0)