Merge pull request #7503 from Budibase/revert-7414-feature/binding-ux-update

Revert "Feature/binding ux update"
This commit is contained in:
Martin McKeaveney 2022-08-30 10:16:12 +01:00 committed by GitHub
commit 7904782c9c
7 changed files with 90 additions and 305 deletions

View File

@ -1,4 +1,4 @@
export default function positionDropdown(element, { anchor, align, maxWidth }) {
export default function positionDropdown(element, { anchor, align }) {
let positionSide = "top"
let maxHeight = 0
let dimensions = getDimensions(anchor)
@ -34,24 +34,13 @@ export default function positionDropdown(element, { anchor, align, maxWidth }) {
}
function calcLeftPosition() {
let left
if (align == "right") {
left = dimensions.left + dimensions.width - dimensions.containerWidth
} else if (align == "right-side") {
left = dimensions.left + dimensions.width
} else {
left = dimensions.left
}
return left
return align === "right"
? dimensions.left + dimensions.width - dimensions.containerWidth
: dimensions.left
}
element.style.position = "absolute"
element.style.zIndex = "9999"
if (maxWidth) {
element.style.maxWidth = `${maxWidth}px`
}
element.style.minWidth = `${dimensions.width}px`
element.style.maxHeight = `${maxHeight.toFixed(0)}px`
element.style.transformOrigin = `center ${positionSide}`
@ -65,8 +54,10 @@ export default function positionDropdown(element, { anchor, align, maxWidth }) {
element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
})
})
resizeObserver.observe(anchor)
resizeObserver.observe(element)
return {
destroy() {
resizeObserver.disconnect()

View File

@ -11,7 +11,6 @@
export let align = "right"
export let portalTarget
export let dataCy
export let maxWidth
export let direction = "bottom"
export let showTip = false
@ -46,7 +45,7 @@
<Portal target={portalTarget}>
<div
tabindex="0"
use:positionDropdown={{ anchor, align, maxWidth }}
use:positionDropdown={{ anchor, align }}
use:clickOutside={hide}
on:keydown={handleEscape}
class={"spectrum-Popover is-open " + (tooltipClasses || "")}

View File

@ -10,7 +10,7 @@ filterTests(['smoke', 'all'], () => {
it("should add a current user binding", () => {
cy.searchAndAddComponent("Paragraph").then(() => {
addSettingBinding("text", ["Current User", "_id"], "Current User._id")
addSettingBinding("text", "Current User._id")
})
})
@ -28,7 +28,7 @@ filterTests(['smoke', 'all'], () => {
const paramName = "foo"
cy.createScreen(`/test/:${paramName}`)
cy.searchAndAddComponent("Paragraph").then(componentId => {
addSettingBinding("text", ["URL", paramName], `URL.${paramName}`)
addSettingBinding("text", `URL.${paramName}`)
// The builder preview pages don't have a real URL, so all we can do
// is check that we were able to bind to the property, and that the
// component exists on the page
@ -47,13 +47,11 @@ filterTests(['smoke', 'all'], () => {
})
})
const addSettingBinding = (setting, bindingCategories, bindingText, clickOption = true) => {
const addSettingBinding = (setting, bindingText, clickOption = true) => {
cy.get(`[data-cy="setting-${setting}"] [data-cy=text-binding-button]`).click()
cy.get(".category-list li").contains(bindingCategories[0])
cy.get(".drawer").within(() => {
if (clickOption) {
cy.get(".category-list li").contains(bindingCategories[0]).click()
cy.get("li.binding").contains(bindingCategories[1]).click()
cy.contains(bindingText).click()
cy.get("textarea").should("have.value", `{{ ${bindingText} }}`)
} else {
cy.get("textarea").type(bindingText)

View File

@ -299,10 +299,7 @@ const getProviderContextBindings = (asset, dataProviders) => {
schema = {}
const values = context.values || []
values.forEach(value => {
schema[value.key] = {
name: value.label,
type: value.type || "string",
}
schema[value.key] = { name: value.label, type: "string" }
})
} else if (context.type === "schema") {
// Schema contexts are generated dynamically depending on their data
@ -362,12 +359,6 @@ const getProviderContextBindings = (asset, dataProviders) => {
providerId,
// Table ID is used by JSON fields to know what table the field is in
tableId: table?._id,
category: component._instanceName,
icon: def.icon,
display: {
name: fieldSchema.name || key,
type: fieldSchema.type,
},
})
})
})
@ -394,9 +385,6 @@ const getUserBindings = () => {
// datasource options, based on bindable properties
fieldSchema,
providerId: "user",
category: "Current User",
icon: "User",
display: fieldSchema,
})
})
return bindings
@ -413,17 +401,11 @@ const getDeviceBindings = () => {
type: "context",
runtimeBinding: `${safeDevice}.${makePropSafe("mobile")}`,
readableBinding: `Device.Mobile`,
category: "Device",
icon: "DevicePhone",
display: { type: "boolean", name: "mobile" },
})
bindings.push({
type: "context",
runtimeBinding: `${safeDevice}.${makePropSafe("tablet")}`,
readableBinding: `Device.Tablet`,
category: "Device",
icon: "DevicePhone",
display: { type: "boolean", name: "tablet" },
})
}
return bindings
@ -447,8 +429,6 @@ const getSelectedRowsBindings = asset => {
"selectedRows"
)}`,
readableBinding: `${table._instanceName}.Selected rows`,
category: "Selected rows",
icon: "ViewRow",
}))
)
@ -480,9 +460,6 @@ const getStateBindings = () => {
type: "context",
runtimeBinding: `${safeState}.${makePropSafe(key)}`,
readableBinding: `State.${key}`,
category: "State",
icon: "AutomatedSegment",
display: { name: key },
}))
}
return bindings
@ -505,17 +482,11 @@ const getUrlBindings = asset => {
type: "context",
runtimeBinding: `${safeURL}.${makePropSafe(param)}`,
readableBinding: `URL.${param}`,
category: "URL",
icon: "RailTop",
display: { type: "string" },
}))
const queryParamsBinding = {
type: "context",
runtimeBinding: makePropSafe("query"),
readableBinding: "Query params",
category: "URL",
icon: "RailTop",
display: { type: "object" },
}
return urlParamBindings.concat([queryParamsBinding])
}
@ -526,9 +497,6 @@ const getRoleBindings = () => {
type: "context",
runtimeBinding: `trim "${role._id}"`,
readableBinding: `Role.${role.name}`,
category: "Role",
icon: "UserGroup",
display: { type: "string", name: role.name },
}
})
}
@ -550,7 +518,6 @@ export const getEventContextBindings = (
// Check if any context bindings are provided by the component for this
// setting
const component = findComponent(asset.props, componentId)
const def = store.actions.components.getDefinition(component?._component)
const settings = getComponentSettings(component?._component)
const eventSetting = settings.find(setting => setting.key === settingKey)
if (eventSetting?.context?.length) {
@ -560,8 +527,6 @@ export const getEventContextBindings = (
runtimeBinding: `${makePropSafe("eventContext")}.${makePropSafe(
contextEntry.key
)}`,
category: component._instanceName,
icon: def.icon,
})
})
}
@ -583,8 +548,6 @@ export const getEventContextBindings = (
bindings.push({
readableBinding: `Action ${idx + 1}.${contextValue.label}`,
runtimeBinding: `actions.${idx}.${contextValue.value}`,
category: "Actions",
icon: "JourneyAction",
})
})
}

View File

@ -9,9 +9,6 @@
Body,
Layout,
Button,
ActionButton,
Icon,
Popover,
} from "@budibase/bbui"
import { createEventDispatcher, onMount } from "svelte"
import {
@ -48,25 +45,9 @@
let jsValue = initialValueJS ? value : null
let hbsValue = initialValueJS ? null : value
let selectedCategory = null
let popover
let popoverAnchor
let hoverTarget
$: usingJS = mode === "JavaScript"
$: searchRgx = new RegExp(search, "ig")
$: categories = Object.entries(groupBy("category", bindings))
$: bindingIcons = bindings?.reduce((acc, ele) => {
if (ele.icon) {
acc[ele.category] = acc[ele.category] || ele.icon
}
return acc
}, {})
$: categoryIcons = { ...bindingIcons, Helpers: "MagicWand" }
$: filteredCategories = categories
.map(([name, categoryBindings]) => ({
name,
@ -74,19 +55,10 @@
return binding.readableBinding.match(searchRgx)
}),
}))
.filter(category => {
return (
category.bindings?.length > 0 &&
(!selectedCategory ? true : selectedCategory === category.name)
)
})
.filter(category => category.bindings?.length > 0)
$: filteredHelpers = helpers?.filter(helper => {
return helper.label.match(searchRgx) || helper.description.match(searchRgx)
})
$: categoryNames = [...categories.map(cat => cat[0]), "Helpers"]
$: codeMirrorHints = bindings?.map(x => `$("${x.readableBinding}")`)
const updateValue = val => {
@ -168,163 +140,58 @@
})
</script>
<span class="detailPopover">
<Popover
align="right-side"
bind:this={popover}
anchor={popoverAnchor}
maxWidth={300}
>
<Layout gap="S">
<div class="helper">
{#if hoverTarget.title}
<div class="helper__name">{hoverTarget.title}</div>
{/if}
{#if hoverTarget.description}
<div class="helper__description">
{@html hoverTarget.description}
</div>
{/if}
{#if hoverTarget.example}
<pre class="helper__example">{hoverTarget.example}</pre>
{/if}
</div>
</Layout>
</Popover>
</span>
<DrawerContent>
<svelte:fragment slot="sidebar">
<Layout noPadding gap="S">
{#if selectedCategory}
<div>
<ActionButton
secondary
icon={"ArrowLeft"}
on:click={() => {
selectedCategory = null
}}
>
Back
</ActionButton>
</div>
{/if}
{#if !selectedCategory}
<div class="container">
<section>
<div class="heading">Search</div>
<Search placeholder="Search" bind:value={search} />
{/if}
{#if !selectedCategory && !search}
<ul class="category-list">
{#each categoryNames as categoryName}
<li
on:click={() => {
selectedCategory = categoryName
}}
>
<Icon name={categoryIcons[categoryName]} />
<span class="category-name">{categoryName} </span>
<span class="category-chevron"><Icon name="ChevronRight" /></span>
</li>
{/each}
</ul>
{/if}
{#if selectedCategory || search}
</section>
{#each filteredCategories as category}
{#if category.bindings?.length}
<div class="cat-heading">
<Icon name={categoryIcons[category.name]} />{category.name}
</div>
<section>
<div class="heading">{category.name}</div>
<ul>
{#each category.bindings as binding}
<li
class="binding"
on:mouseenter={e => {
popoverAnchor = e.target
if (!binding.description) {
return
}
hoverTarget = {
title: binding.display.name || binding.fieldSchema.name,
description: binding.description,
}
popover.show()
e.stopPropagation()
}}
on:mouseleave={() => {
popover.hide()
popoverAnchor = null
hoverTarget = null
}}
on:focus={() => {}}
on:blur={() => {}}
on:click={() => addBinding(binding)}
>
<span class="binding__label">
{#if binding.display?.name}
{binding.display.name}
{:else if binding.fieldSchema?.name}
{binding.fieldSchema?.name}
{:else}
{binding.readableBinding}
<li on:click={() => addBinding(binding)}>
<span class="binding__label">{binding.readableBinding}</span>
{#if binding.type}
<span class="binding__type">{binding.type}</span>
{/if}
</span>
{#if binding.display?.type || binding.fieldSchema?.type}
<span class="binding__typeWrap">
<span class="binding__type">
{binding.display?.type || binding.fieldSchema?.type}
</span>
</span>
{#if binding.description}
<br />
<div class="binding__description">
{binding.description || ""}
</div>
{/if}
</li>
{/each}
</ul>
</section>
{/if}
{/each}
{#if selectedCategory === "Helpers" || search}
{#if filteredHelpers?.length}
<section>
<div class="heading">Helpers</div>
<ul class="helpers">
<ul>
{#each filteredHelpers as helper}
<li
class="binding"
on:click={() => addHelper(helper, usingJS)}
on:mouseenter={e => {
popoverAnchor = e.target
if (!helper.displayText && helper.description) {
return
}
hoverTarget = {
title: helper.displayText,
description: helper.description,
example: getHelperExample(helper, usingJS),
}
popover.show()
e.stopPropagation()
}}
on:mouseleave={() => {
popover.hide()
popoverAnchor = null
hoverTarget = null
}}
on:focus={() => {}}
on:blur={() => {}}
>
<span class="binding__label">{helper.displayText}</span>
<span class="binding__typeWrap">
<span class="binding__type">function</span>
</span>
<li on:click={() => addHelper(helper, usingJS)}>
<div class="helper">
<div class="helper__name">{helper.displayText}</div>
<div class="helper__description">
{@html helper.description}
</div>
<pre class="helper__example">{getHelperExample(
helper,
usingJS
)}</pre>
</div>
</li>
{/each}
</ul>
</section>
{/if}
{/if}
{/if}
</Layout>
</div>
</svelte:fragment>
<div class="main">
<Tabs selected={mode} on:select={onChangeMode}>
@ -374,35 +241,6 @@
</DrawerContent>
<style>
ul.helpers li * {
pointer-events: none;
}
ul.category-list li {
display: flex;
gap: var(--spacing-m);
align-items: center;
}
ul.category-list .category-name {
font-weight: 600;
text-transform: capitalize;
}
ul.category-list .category-chevron {
flex: 1;
text-align: right;
}
ul.category-list .category-chevron :global(div.icon),
.cat-heading :global(div.icon) {
display: inline-block;
}
li.binding {
display: flex;
align-items: center;
}
li.binding .binding__typeWrap {
flex: 1;
text-align: right;
text-transform: capitalize;
}
.main :global(textarea) {
min-height: 202px !important;
}
@ -413,20 +251,23 @@
padding: var(--spacing-s) var(--spacing-xl);
}
.heading,
.cat-heading {
.container {
margin: calc(-1 * var(--spacing-xl));
}
.heading {
font-size: var(--font-size-s);
font-weight: 600;
text-transform: uppercase;
color: var(--spectrum-global-color-gray-600);
padding: var(--spacing-xl) 0 var(--spacing-m) 0;
}
.cat-heading {
display: flex;
gap: var(--spacing-m);
align-items: center;
section {
padding: 0 var(--spacing-xl) var(--spacing-xl) var(--spacing-xl);
}
section:not(:first-child) {
border-top: var(--border-light);
}
ul {
list-style: none;
padding: 0;
@ -437,7 +278,7 @@
font-size: var(--font-size-s);
padding: var(--spacing-m);
border-radius: 4px;
background-color: var(--spectrum-global-color-gray-200);
border: var(--border-light);
transition: background-color 130ms ease-in-out, color 130ms ease-in-out,
border-color 130ms ease-in-out;
word-wrap: break-word;
@ -451,14 +292,22 @@
li:hover {
color: var(--spectrum-global-color-gray-900);
background-color: var(--spectrum-global-color-gray-50);
border-color: var(--spectrum-global-color-gray-500);
cursor: pointer;
}
li:hover :global(*) {
color: var(--spectrum-global-color-gray-900) !important;
}
.binding__label {
font-weight: 600;
text-transform: capitalize;
}
.binding__description {
color: var(--spectrum-global-color-gray-700);
margin: 0.5rem 0 0 0;
white-space: normal;
}
.binding__type {
font-family: monospace;
background-color: var(--spectrum-global-color-gray-200);

View File

@ -15,6 +15,7 @@
}
return bindings?.map(binding => ({
...binding,
category: "Bindable Values",
type: null,
}))
}

View File

@ -544,8 +544,7 @@
"values": [
{
"label": "Row Index",
"key": "index",
"type": "number"
"key": "index"
}
]
}
@ -2315,23 +2314,19 @@
"values": [
{
"label": "Value",
"key": "__value",
"type": "object"
"key": "__value"
},
{
"label": "Valid",
"key": "__valid",
"type": "boolean"
"key": "__valid"
},
{
"label": "Current Step",
"key": "__currentStep",
"type": "number"
"key": "__currentStep"
},
{
"label": "Current Step Valid",
"key": "__currentStepValid",
"type": "boolean"
"key": "__currentStepValid"
}
]
},
@ -3555,28 +3550,23 @@
"values": [
{
"label": "Rows",
"key": "rows",
"type": "array"
"key": "rows"
},
{
"label": "Extra Info",
"key": "info",
"type": "string"
"key": "info"
},
{
"label": "Rows Length",
"key": "rowsLength",
"type": "number"
"key": "rowsLength"
},
{
"label": "Schema",
"key": "schema",
"type": "object"
"key": "schema"
},
{
"label": "Page Number",
"key": "pageNumber",
"type": "number"
"key": "pageNumber"
}
]
}
@ -4338,28 +4328,23 @@
"values": [
{
"label": "Rows",
"key": "rows",
"type": "array"
"key": "rows"
},
{
"label": "Extra Info",
"key": "info",
"type": "string"
"key": "info"
},
{
"label": "Rows Length",
"key": "rowsLength",
"type": "number"
"key": "rowsLength"
},
{
"label": "Schema",
"key": "schema",
"type": "object"
"key": "schema"
},
{
"label": "Page Number",
"key": "pageNumber",
"type": "number"
"key": "pageNumber"
}
]
},
@ -4369,8 +4354,7 @@
"values": [
{
"label": "Row Index",
"key": "index",
"type": "number"
"key": "index"
}
]
},