diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index f8087d8a39..f7eccd5242 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -24,17 +24,23 @@ let selectedRows = [] let customRenderers = [] + let parsedSchema = {} + + $: if (schema) { + parsedSchema = Object.keys(schema).reduce((acc, key) => { + acc[key] = + typeof schema[key] === "string" ? { type: schema[key] } : schema[key] + + if (!canBeSortColumn(acc[key].type)) { + acc[key].sortable = false + } + return acc + }, {}) + } $: selectedRows, dispatch("selectionUpdated", selectedRows) $: isUsersTable = tableId === TableNames.USERS $: data && resetSelectedRows() - $: { - Object.values(schema || {}).forEach(col => { - if (!canBeSortColumn(col.type)) { - col.sortable = false - } - }) - } $: { if (isUsersTable) { customRenderers = [ @@ -44,24 +50,24 @@ }, ] UNEDITABLE_USER_FIELDS.forEach(field => { - if (schema[field]) { - schema[field].editable = false + if (parsedSchema[field]) { + parsedSchema[field].editable = false } }) - if (schema.email) { - schema.email.displayName = "Email" + if (parsedSchema.email) { + parsedSchema.email.displayName = "Email" } - if (schema.roleId) { - schema.roleId.displayName = "Role" + if (parsedSchema.roleId) { + parsedSchema.roleId.displayName = "Role" } - if (schema.firstName) { - schema.firstName.displayName = "First Name" + if (parsedSchema.firstName) { + parsedSchema.firstName.displayName = "First Name" } - if (schema.lastName) { - schema.lastName.displayName = "Last Name" + if (parsedSchema.lastName) { + parsedSchema.lastName.displayName = "Last Name" } - if (schema.status) { - schema.status.displayName = "Status" + if (parsedSchema.status) { + parsedSchema.status.displayName = "Status" } } } @@ -97,7 +103,7 @@
{ @@ -61,7 +62,7 @@
+ import DraggableList from "../DraggableList/DraggableList.svelte" + import ButtonSetting from "./ButtonSetting.svelte" + import { createEventDispatcher } from "svelte" + import { store } from "builderStore" + import { Helpers } from "@budibase/bbui" + + export let componentBindings + export let bindings + export let value + + const dispatch = createEventDispatcher() + + let focusItem + + $: buttonList = sanitizeValue(value) || [] + $: buttonCount = buttonList.length + $: itemProps = { + componentBindings: componentBindings || [], + bindings, + removeButton, + canRemove: buttonCount > 1, + } + + const sanitizeValue = val => { + return val?.map(button => { + return button._component ? button : buildPseudoInstance(button) + }) + } + + const processItemUpdate = e => { + const updatedField = e.detail + const newButtonList = [...buttonList] + const fieldIdx = newButtonList.findIndex(pSetting => { + return pSetting._id === updatedField?._id + }) + if (fieldIdx === -1) { + newButtonList.push(updatedField) + } else { + newButtonList[fieldIdx] = updatedField + } + dispatch("change", newButtonList) + } + + const listUpdated = e => { + dispatch("change", [...e.detail]) + } + + const buildPseudoInstance = cfg => { + return store.actions.components.createInstance( + `@budibase/standard-components/button`, + { + _instanceName: Helpers.uuid(), + text: cfg.text, + type: cfg.type || "primary", + }, + {} + ) + } + + const addButton = () => { + const newButton = buildPseudoInstance({ + text: `Button ${buttonCount + 1}`, + }) + dispatch("change", [...buttonList, newButton]) + focusItem = newButton._id + } + + const removeButton = id => { + dispatch( + "change", + buttonList.filter(button => button._id !== id) + ) + } + + +
+ {#if buttonCount} + 1} + /> + + + {/if} +
+ + diff --git a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte new file mode 100644 index 0000000000..a05fd9a39b --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte @@ -0,0 +1,64 @@ + + +
+
+ +
{readableText || "Button"}
+
+
+ removeButton(item._id)} + /> +
+
+ + diff --git a/packages/builder/src/components/design/settings/controls/DraggableList.svelte b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte similarity index 82% rename from packages/builder/src/components/design/settings/controls/DraggableList.svelte rename to packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte index c8395b2a1f..1992299e90 100644 --- a/packages/builder/src/components/design/settings/controls/DraggableList.svelte +++ b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte @@ -1,10 +1,10 @@
- -
{item.label || item.field}
+ > +
+ + {item.field} +
+ +
{readableText}
@@ -53,4 +81,20 @@ .list-item-body { justify-content: space-between; } + .type-icon { + display: flex; + gap: var(--spacing-m); + margin: var(--spacing-xl); + margin-bottom: 0px; + height: var(--spectrum-alias-item-height-m); + padding: 0px var(--spectrum-alias-item-padding-m); + border-width: var(--spectrum-actionbutton-border-size); + border-radius: var(--spectrum-alias-border-radius-regular); + border: 1px solid + var( + --spectrum-actionbutton-m-border-color, + var(--spectrum-alias-border-color) + ); + align-items: center; + } diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsPanel.svelte index 17eadb99bd..affa115ca2 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsPanel.svelte @@ -91,7 +91,12 @@ /> {/if} {#if section == "styles"} - + { + const getSections = (instance, definition, isScreen, tag, includeHidden) => { const settings = definition?.settings ?? [] - const generalSettings = settings.filter(setting => !setting.section) - const customSections = settings.filter(setting => setting.section) + const generalSettings = settings.filter( + setting => !setting.section && setting.tag === tag + ) + const customSections = settings.filter( + setting => setting.section && setting.tag === tag + ) let sections = [ - { - name: "General", - settings: generalSettings, - }, + ...(generalSettings?.length + ? [ + { + name: "General", + settings: generalSettings, + }, + ] + : []), ...(customSections || []), ] @@ -147,7 +157,7 @@ - {:else if idx === 0 && section.name === "General" && componentDefinition.info} + {:else if idx === 0 && section.name === "General" && componentDefinition?.info && !tag} {/if} {/each} -{#if componentDefinition?.block} +{#if componentDefinition?.block && !tag} diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/DesignSection.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/DesignSection.svelte index 444ded7e1f..def1fcf24b 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/DesignSection.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/DesignSection.svelte @@ -1,10 +1,12 @@ + + + {#if styles?.length > 0} {#each styles as style} + import BlockComponent from "../BlockComponent.svelte" + import Block from "../Block.svelte" + + export let buttons = [] + export let direction + export let hAlign + export let vAlign + export let gap = "S" + + + + + {#each buttons as { text, type, quiet, disabled, onClick, size }} + + {/each} + + diff --git a/packages/client/src/components/app/index.js b/packages/client/src/components/app/index.js index 060c15a857..97df3741e1 100644 --- a/packages/client/src/components/app/index.js +++ b/packages/client/src/components/app/index.js @@ -19,6 +19,7 @@ export { default as dataprovider } from "./DataProvider.svelte" export { default as divider } from "./Divider.svelte" export { default as screenslot } from "./ScreenSlot.svelte" export { default as button } from "./Button.svelte" +export { default as buttongroup } from "./ButtonGroup.svelte" export { default as repeater } from "./Repeater.svelte" export { default as text } from "./Text.svelte" export { default as layout } from "./Layout.svelte" diff --git a/packages/worker/src/api/routes/global/tests/groups.spec.ts b/packages/worker/src/api/routes/global/tests/groups.spec.ts index afeaae952c..8f0739a812 100644 --- a/packages/worker/src/api/routes/global/tests/groups.spec.ts +++ b/packages/worker/src/api/routes/global/tests/groups.spec.ts @@ -1,7 +1,7 @@ import { events } from "@budibase/backend-core" import { generator } from "@budibase/backend-core/tests" import { structures, TestConfiguration, mocks } from "../../../../tests" -import { UserGroup } from "@budibase/types" +import { User, UserGroup } from "@budibase/types" mocks.licenses.useGroups() @@ -231,4 +231,39 @@ describe("/api/global/groups", () => { }) }) }) + + describe("with global builder role", () => { + let builder: User + let group: UserGroup + + beforeAll(async () => { + builder = await config.createUser({ + builder: { global: true }, + admin: { global: false }, + }) + await config.createSession(builder) + + let resp = await config.api.groups.saveGroup( + structures.groups.UserGroup() + ) + group = resp.body as UserGroup + }) + + it("find should return 200", async () => { + await config.withUser(builder, async () => { + await config.api.groups.searchUsers(group._id!, { + emailSearch: `user1`, + }) + }) + }) + + it("update should return 200", async () => { + await config.withUser(builder, async () => { + await config.api.groups.updateGroupUsers(group._id!, { + add: [builder._id!], + remove: [], + }) + }) + }) + }) }) diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index 7e9792c9e3..d4fcbeebd6 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -190,6 +190,16 @@ class TestConfiguration { } } + async withUser(user: User, f: () => Promise) { + const oldUser = this.user + this.user = user + try { + await f() + } finally { + this.user = oldUser + } + } + authHeaders(user: User) { const authToken: AuthToken = { userId: user._id!, @@ -257,9 +267,10 @@ class TestConfiguration { }) } - async createUser(user?: User) { - if (!user) { - user = structures.users.user() + async createUser(opts?: Partial) { + let user = structures.users.user() + if (user) { + user = { ...user, ...opts } } const response = await this._req(user, null, controllers.users.save) const body = response as SaveUserResponse diff --git a/packages/worker/src/tests/structures/groups.ts b/packages/worker/src/tests/structures/groups.ts index b0d6bb8fc0..d39dd74eb8 100644 --- a/packages/worker/src/tests/structures/groups.ts +++ b/packages/worker/src/tests/structures/groups.ts @@ -1,8 +1,8 @@ import { generator } from "@budibase/backend-core/tests" import { db } from "@budibase/backend-core" -import { UserGroupRoles } from "@budibase/types" +import { UserGroup as UserGroupType, UserGroupRoles } from "@budibase/types" -export const UserGroup = () => { +export function UserGroup(): UserGroupType { const appsCount = generator.integer({ min: 0, max: 3 }) const roles = Array.from({ length: appsCount }).reduce( (p: UserGroupRoles, v) => { @@ -14,13 +14,11 @@ export const UserGroup = () => { {} ) - let group = { - apps: [], + return { color: generator.color(), icon: generator.word(), name: generator.word(), roles: roles, users: [], } - return group }