Add initial work on merging settings and design panels, improve code structure, improve styles

This commit is contained in:
Andrew Kingston 2021-06-22 09:14:17 +01:00
parent 0f691e5484
commit aee876a681
15 changed files with 255 additions and 342 deletions

View File

@ -2,12 +2,10 @@
import Icon from "../Icon/Icon.svelte" import Icon from "../Icon/Icon.svelte"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
export let name
export let show = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
export let thin = false
export let name,
show = false
const onHeaderClick = () => { const onHeaderClick = () => {
show = !show show = !show
if (show) { if (show) {
@ -16,12 +14,10 @@
} }
</script> </script>
<div class="property-group-container" class:thin> <div class="property-group-container">
<div class="property-group-name" on:click={onHeaderClick}> <div class="property-group-name" on:click={onHeaderClick}>
<div class:thin class="name">{name}</div> <div class="name">{name}</div>
<div class="icon"> <Icon size="S" name={show ? "Remove" : "Add"} />
<Icon size="S" name={show ? "Remove" : "Add"} />
</div>
</div> </div>
<div class="property-panel" class:show> <div class="property-panel" class:show>
<slot /> <slot />
@ -32,10 +28,9 @@
.property-group-container { .property-group-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: auto; justify-content: flex-start;
justify-content: center; align-items: stretch;
border-radius: var(--border-radius-m); border-bottom: var(--border-light);
font-family: var(--font-sans);
} }
.property-group-name { .property-group-name {
@ -45,42 +40,38 @@
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: var(--spacing-m) var(--spacing-xl);
color: var(--spectrum-global-color-gray-600);
transition: color 130ms ease-in-out;
}
.property-group-name:hover {
color: var(--spectrum-global-color-gray-900);
} }
.name { .name {
text-align: left; text-align: left;
font-size: 14px; font-size: var(--font-size-s);
font-weight: 600; font-weight: 600;
letter-spacing: 0.14px; letter-spacing: 0.14px;
color: var(--ink);
flex: 1 1 auto; flex: 1 1 auto;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
text-transform: capitalize; text-transform: uppercase;
white-space: nowrap; white-space: nowrap;
user-select: none; user-select: none;
} }
.name.thin {
font-size: var(--spectrum-global-dimension-font-size-75);
}
.icon {
flex: 0 0 20px;
text-align: center;
}
.property-panel { .property-panel {
/* height: 0px;
overflow: hidden; */
display: none; display: none;
padding: var(--spacing-s) var(--spacing-xl) var(--spacing-xl)
var(--spacing-xl);
} }
.show { .show {
/* overflow: auto;
height: auto; */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; justify-content: flex-start;
margin-top: var(--spacing-m); align-items: stretch;
gap: var(--spacing-m);
} }
</style> </style>

View File

@ -5,6 +5,8 @@
export let selected export let selected
export let vertical = false export let vertical = false
export let noPadding = false
let _id = id() let _id = id()
const tab = writable({ title: selected, id: _id }) const tab = writable({ title: selected, id: _id })
setContext("tab", tab) setContext("tab", tab)
@ -63,14 +65,17 @@
{/if} {/if}
</div> </div>
<div class="spectrum-Tabs-content spectrum-Tabs-content-{_id}" /> <div
class="spectrum-Tabs-content spectrum-Tabs-content-{_id}"
class:noPadding
/>
<style> <style>
.spectrum-Tabs { .spectrum-Tabs {
padding-left: var(--spacing-xl); padding-left: var(--spacing-xl);
padding-right: var(--spacing-xl); padding-right: var(--spacing-xl);
position: relative; position: relative;
border-width: 1px !important; border-bottom: var(--border-light);
} }
.spectrum-Tabs-content { .spectrum-Tabs-content {
margin-top: var(--spectrum-global-dimension-static-size-150); margin-top: var(--spectrum-global-dimension-static-size-150);
@ -81,4 +86,7 @@
.spectrum-Tabs--horizontal .spectrum-Tabs-selectionIndicator { .spectrum-Tabs--horizontal .spectrum-Tabs-selectionIndicator {
bottom: 0 !important; bottom: 0 !important;
} }
.noPadding {
margin: 0;
}
</style> </style>

View File

@ -488,12 +488,12 @@ export const getFrontendStore = () => {
}) })
await Promise.all(promises) await Promise.all(promises)
}, },
updateStyle: async (type, name, value) => { updateStyle: async (name, value) => {
const selected = get(selectedComponent) const selected = get(selectedComponent)
if (value == null || value === "") { if (value == null || value === "") {
delete selected._styles[type][name] delete selected._styles.normal[name]
} else { } else {
selected._styles[type][name] = value selected._styles.normal[name] = value
} }
await store.actions.preview.saveSelected() await store.actions.preview.saveSelected()
}, },

View File

@ -0,0 +1,50 @@
<script>
import { DetailSummary, ActionButton } from "@budibase/bbui"
import { currentAsset, store } from "builderStore"
import { findClosestMatchingComponent } from "builderStore/storeUtils"
import { makeDatasourceFormComponents } from "builderStore/store/screenTemplates/utils/commonComponents"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
export let openSection
export let componentDefinition
export let componentInstance
let confirmResetFieldsDialog
const resetFormFields = () => {
const form = findClosestMatchingComponent(
$currentAsset.props,
componentInstance._id,
component => component._component.endsWith("/form")
)
const dataSource = form?.dataSource
const fields = makeDatasourceFormComponents(dataSource)
store.actions.components.updateProp(
"_children",
fields.map(field => field.json())
)
}
</script>
<DetailSummary name="Actions" on:open show={openSection === "actions"}>
<ActionButton secondary wide on:click={store.actions.components.resetStyles}>
Reset styles
</ActionButton>
{#if componentDefinition?.component?.endsWith("/fieldgroup")}
<ActionButton
secondary
wide
on:click={() => confirmResetFieldsDialog?.show()}
>
Update form fields
</ActionButton>
{/if}
</DetailSummary>
<ConfirmDialog
bind:this={confirmResetFieldsDialog}
body={`All components inside this group will be deleted and replaced with fields to match the schema. Are you sure you want to update this Field Group?`}
okText="Update"
onOk={resetFormFields}
title="Confirm Form Field Update"
/>

View File

@ -1,40 +0,0 @@
<script>
export let categories = []
export let selectedCategory = {}
export let onClick = () => {}
</script>
<div class="tabs">
{#each categories as category}
<li
data-cy={category.name}
on:click={() => onClick(category)}
class:active={selectedCategory === category}
>
{category.name}
</li>
{/each}
</div>
<style>
.tabs {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
list-style: none;
font-size: var(--font-size-m);
font-weight: 600;
height: 24px;
}
li {
color: var(--grey-5);
cursor: pointer;
margin-right: 20px;
}
.active {
color: var(--ink);
}
</style>

View File

@ -0,0 +1,33 @@
<script>
import { TextArea, DetailSummary } from "@budibase/bbui"
import { store } from "builderStore"
export let componentInstance
export let openSection
function onChange(css) {
store.actions.components.updateCustomStyle(css)
}
</script>
<DetailSummary
name={`Custom Styles${componentInstance?._styles?.custom ? " *" : ""}`}
on:open
show={openSection === "custom"}
>
<div class="custom-styles">
<TextArea
value={componentInstance?._styles?.custom}
on:change={event => onChange(event.detail)}
placeholder="Enter some CSS..."
/>
</div>
</DetailSummary>
<style>
.custom-styles :global(textarea) {
font-family: monospace;
min-height: 120px;
font-size: var(--font-size-xs);
}
</style>

View File

@ -0,0 +1,42 @@
<script>
import { DetailSummary } from "@budibase/bbui"
import PropertyGroup from "./PropertyControls/PropertyGroup.svelte"
import { allStyles } from "./componentStyles"
import { store } from "builderStore"
export let componentDefinition
export let componentInstance
export let openSection
let selectedCategory = "normal"
let currentGroup
$: groups = componentDefinition?.styleable ? Object.keys(allStyles) : []
</script>
<DetailSummary name="Design" show={openSection === "design"} on:open>
{#if groups.length > 0}
{#each groups as groupName}
<PropertyGroup
name={groupName}
properties={allStyles[groupName]}
styleCategory={selectedCategory}
onStyleChanged={store.actions.components.updateStyle}
{componentInstance}
open={currentGroup === groupName}
on:open={() => (currentGroup = groupName)}
/>
{/each}
{:else}
<div class="no-design">
This component doesn't have any design properties.
</div>
{/if}
</DetailSummary>
<style>
.no-design {
font-size: var(--spectrum-global-dimension-font-size-75);
color: var(--grey-6);
}
</style>

View File

@ -1,111 +0,0 @@
<script>
import { TextArea, DetailSummary, Button } from "@budibase/bbui"
import PropertyGroup from "./PropertyControls/PropertyGroup.svelte"
import FlatButtonGroup from "./PropertyControls/FlatButtonGroup"
import { allStyles } from "./componentStyles"
export let componentDefinition = {}
export let componentInstance = {}
export let onStyleChanged = () => {}
export let onCustomStyleChanged = () => {}
export let onResetStyles = () => {}
let selectedCategory = "normal"
let currentGroup
function onChange(category) {
selectedCategory = category
}
const buttonProps = [
{ value: "normal", text: "Normal" },
{ value: "hover", text: "Hover" },
{ value: "active", text: "Active" },
]
$: groups = componentDefinition?.styleable ? Object.keys(allStyles) : []
</script>
<div class="container">
<div class="state-categories">
<FlatButtonGroup value={selectedCategory} {buttonProps} {onChange} />
</div>
<div class="positioned-wrapper">
<div class="property-groups">
{#if groups.length > 0}
{#each groups as groupName}
<PropertyGroup
name={groupName}
properties={allStyles[groupName]}
styleCategory={selectedCategory}
{onStyleChanged}
{componentInstance}
open={currentGroup === groupName}
on:open={() => (currentGroup = groupName)}
/>
{/each}
<DetailSummary
name={`Custom Styles${componentInstance._styles.custom ? " *" : ""}`}
on:open={() => (currentGroup = "custom")}
show={currentGroup === "custom"}
thin
>
<div class="custom-styles">
<TextArea
value={componentInstance._styles.custom}
on:change={event => onCustomStyleChanged(event.detail)}
placeholder="Enter some CSS..."
/>
</div>
</DetailSummary>
<Button secondary wide on:click={onResetStyles}>Reset Styles</Button>
{:else}
<div class="no-design">
This component doesn't have any design properties.
</div>
{/if}
</div>
</div>
</div>
<style>
.container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
gap: var(--spacing-l);
}
.positioned-wrapper {
position: relative;
display: flex;
min-height: 0;
flex: 1 1 auto;
}
.property-groups {
flex: 1;
overflow-y: auto;
min-height: 0;
margin: 0 -20px;
padding: 0 20px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-m);
}
.no-design {
font-size: var(--font-size-xs);
color: var(--grey-5);
}
.custom-styles :global(textarea) {
font-family: monospace;
min-height: 120px;
font-size: var(--font-size-xs);
}
</style>

View File

@ -3,11 +3,16 @@
import { store, selectedComponent, currentAsset } from "builderStore" import { store, selectedComponent, currentAsset } from "builderStore"
import { Tabs, Tab } from "@budibase/bbui" import { Tabs, Tab } from "@budibase/bbui"
import { FrontendTypes } from "constants" import { FrontendTypes } from "constants"
import DesignView from "./DesignView.svelte" import SettingsSection from "./SettingsSection.svelte"
import SettingsView from "./SettingsView.svelte" import DesignSection from "./DesignSection.svelte"
import CustomStylesSection from "./CustomStylesSection.svelte"
import ActionsSection from "./ActionsSection.svelte"
import { setWith } from "lodash" import { setWith } from "lodash"
$: definition = store.actions.components.getDefinition( let openSection = "settings"
$: componentInstance = $selectedComponent
$: componentDefinition = store.actions.components.getDefinition(
$selectedComponent?._component $selectedComponent?._component
) )
$: isComponentOrScreen = $: isComponentOrScreen =
@ -16,10 +21,6 @@
$: isNotScreenslot = !$selectedComponent._component.endsWith("screenslot") $: isNotScreenslot = !$selectedComponent._component.endsWith("screenslot")
$: showDisplayName = isComponentOrScreen && isNotScreenslot $: showDisplayName = isComponentOrScreen && isNotScreenslot
const onStyleChanged = store.actions.components.updateStyle
const onCustomStyleChanged = store.actions.components.updateCustomStyle
const onResetStyles = store.actions.components.resetStyles
function setAssetProps(name, value) { function setAssetProps(name, value) {
const selectedAsset = get(currentAsset) const selectedAsset = get(currentAsset)
store.update(state => { store.update(state => {
@ -37,48 +38,34 @@
} }
</script> </script>
<Tabs selected="Settings"> <Tabs selected="Settings" noPadding>
<Tab title="Settings"> <Tab title="Settings">
<div class="tab-content-padding"> <SettingsSection
{#if definition && definition.name} {componentInstance}
<div class="instance-name">{definition.name}</div> {componentDefinition}
{/if} {showDisplayName}
<SettingsView onScreenPropChange={setAssetProps}
componentInstance={$selectedComponent} assetInstance={$store.currentView !== "component" && $currentAsset}
componentDefinition={definition} {openSection}
{showDisplayName} on:open={() => (openSection = "settings")}
onChange={store.actions.components.updateProp} />
onScreenPropChange={setAssetProps} <DesignSection
assetInstance={$store.currentView !== "component" && $currentAsset} {componentInstance}
/> {componentDefinition}
</div> {openSection}
</Tab> on:open={() => (openSection = "design")}
<Tab title="Design"> />
<div class="tab-content-padding"> <CustomStylesSection
{#if definition && definition.name} {componentInstance}
<div class="instance-name">{definition.name}</div> {componentDefinition}
{/if} {openSection}
<DesignView on:open={() => (openSection = "custom")}
componentInstance={$selectedComponent} />
componentDefinition={definition} <ActionsSection
{onStyleChanged} {componentInstance}
{onCustomStyleChanged} {componentDefinition}
{onResetStyles} {openSection}
/> on:open={() => (openSection = "actions")}
</div> />
</Tab> </Tab>
</Tabs> </Tabs>
<style>
.tab-content-padding {
padding: 0 var(--spacing-xl);
}
.instance-name {
font-size: var(--spectrum-global-dimension-font-size-75);
margin-bottom: var(--spacing-m);
margin-top: var(--spacing-xs);
font-weight: 600;
color: var(--grey-7);
}
</style>

View File

@ -1,5 +1,5 @@
<script> <script>
import { Button, Icon, Drawer } from "@budibase/bbui" import { Button, Icon, Drawer, Label } from "@budibase/bbui"
import { store, currentAsset } from "builderStore" import { store, currentAsset } from "builderStore"
import { import {
getBindableProperties, getBindableProperties,
@ -70,7 +70,11 @@
</script> </script>
<div class="property-control" bind:this={anchor} data-cy={`setting-${key}`}> <div class="property-control" bind:this={anchor} data-cy={`setting-${key}`}>
<div class="label">{label}</div> {#if type !== "boolean"}
<div class="label">
<Label>{label}</Label>
</div>
{/if}
<div data-cy={`${key}-prop-control`} class="control"> <div data-cy={`${key}-prop-control`} class="control">
<svelte:component <svelte:component
this={control} this={control}
@ -79,63 +83,56 @@
updateOnChange={false} updateOnChange={false}
on:change={handleChange} on:change={handleChange}
onChange={handleChange} onChange={handleChange}
name={key}
text={label}
{type} {type}
{...props} {...props}
name={key}
/> />
</div> {#if bindable && !key.startsWith("_") && type === "text"}
{#if bindable && !key.startsWith("_") && type === "text"} <div
<div class="icon"
class="icon" data-cy={`${key}-binding-button`}
data-cy={`${key}-binding-button`} on:click={bindingDrawer.show}
on:click={bindingDrawer.show}
>
<Icon size="S" name="FlashOn" />
</div>
<Drawer bind:this={bindingDrawer} title={capitalise(key)}>
<svelte:fragment slot="description">
Add the objects on the left to enrich your text.
</svelte:fragment>
<Button cta slot="buttons" disabled={!valid} on:click={handleClose}
>Save</Button
> >
<BindingPanel <Icon size="S" name="FlashOn" />
slot="body" </div>
bind:valid <Drawer bind:this={bindingDrawer} title={capitalise(key)}>
value={safeValue} <svelte:fragment slot="description">
close={handleClose} Add the objects on the left to enrich your text.
on:update={e => (temporaryBindableValue = e.detail)} </svelte:fragment>
{bindableProperties} <Button cta slot="buttons" disabled={!valid} on:click={handleClose}>
/> Save
</Drawer> </Button>
{/if} <BindingPanel
slot="body"
bind:valid
value={safeValue}
close={handleClose}
on:update={e => (temporaryBindableValue = e.detail)}
{bindableProperties}
/>
</Drawer>
{/if}
</div>
</div> </div>
<style> <style>
.property-control { .property-control {
position: relative; position: relative;
display: flex; display: flex;
flex-flow: row; flex-direction: column;
align-items: center; justify-content: flex-start;
align-items: stretch;
} }
.label { .label {
display: flex;
align-items: center;
font-size: 12px;
font-weight: 400;
flex: 0 0 80px;
text-align: left;
color: var(--ink);
margin-right: auto;
text-transform: capitalize; text-transform: capitalize;
padding-top: var(--spectrum-global-dimension-size-50);
padding-bottom: var(--spectrum-global-dimension-size-65);
} }
.control { .control {
flex: 1; position: relative;
display: inline-block;
padding-left: 2px;
width: 0;
} }
.icon { .icon {

View File

@ -1,11 +1,8 @@
<script> <script>
import { get } from "lodash" import { get } from "lodash"
import { isEmpty } from "lodash/fp" import { isEmpty } from "lodash/fp"
import { Button, Checkbox, Input, Select } from "@budibase/bbui" import { Checkbox, Input, Select, DetailSummary } from "@budibase/bbui"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import { store } from "builderStore"
import { currentAsset } from "builderStore"
import { findClosestMatchingComponent } from "builderStore/storeUtils"
import { makeDatasourceFormComponents } from "builderStore/store/screenTemplates/utils/commonComponents"
import PropertyControl from "./PropertyControls/PropertyControl.svelte" import PropertyControl from "./PropertyControls/PropertyControl.svelte"
import LayoutSelect from "./PropertyControls/LayoutSelect.svelte" import LayoutSelect from "./PropertyControls/LayoutSelect.svelte"
import RoleSelect from "./PropertyControls/RoleSelect.svelte" import RoleSelect from "./PropertyControls/RoleSelect.svelte"
@ -33,9 +30,9 @@
export let componentDefinition = {} export let componentDefinition = {}
export let componentInstance = {} export let componentInstance = {}
export let assetInstance export let assetInstance
export let onChange = () => {}
export let onScreenPropChange = () => {} export let onScreenPropChange = () => {}
export let showDisplayName = false export let showDisplayName = false
export let openSection
const layoutDefinition = [] const layoutDefinition = []
const screenDefinition = [ const screenDefinition = [
@ -44,12 +41,12 @@
{ key: "routing.roleId", label: "Access", control: RoleSelect }, { key: "routing.roleId", label: "Access", control: RoleSelect },
{ key: "layoutId", label: "Layout", control: LayoutSelect }, { key: "layoutId", label: "Layout", control: LayoutSelect },
] ]
let confirmResetFieldsDialog
$: settings = componentDefinition?.settings ?? [] $: settings = componentDefinition?.settings ?? []
$: isLayout = assetInstance && assetInstance.favicon $: isLayout = assetInstance && assetInstance.favicon
$: assetDefinition = isLayout ? layoutDefinition : screenDefinition $: assetDefinition = isLayout ? layoutDefinition : screenDefinition
const updateProp = store.actions.components.updateProp
const controlMap = { const controlMap = {
text: Input, text: Input,
select: Select, select: Select,
@ -91,27 +88,13 @@
} }
return true return true
} }
const onInstanceNameChange = name => {
onChange("_instanceName", name)
}
const resetFormFields = () => {
const form = findClosestMatchingComponent(
$currentAsset.props,
componentInstance._id,
component => component._component.endsWith("/form")
)
const dataSource = form?.dataSource
const fields = makeDatasourceFormComponents(dataSource)
onChange(
"_children",
fields.map(field => field.json())
)
}
</script> </script>
<div class="settings-view-container"> <DetailSummary
name={componentDefinition.name}
on:open
show={openSection === "settings"}
>
{#if assetInstance} {#if assetInstance}
{#each assetDefinition as def (`${componentInstance._id}-${def.key}`)} {#each assetDefinition as def (`${componentInstance._id}-${def.key}`)}
<PropertyControl <PropertyControl
@ -124,7 +107,6 @@
/> />
{/each} {/each}
{/if} {/if}
{#if showDisplayName} {#if showDisplayName}
<PropertyControl <PropertyControl
bindable={false} bindable={false}
@ -132,10 +114,9 @@
label="Name" label="Name"
key="_instanceName" key="_instanceName"
value={componentInstance._instanceName} value={componentInstance._instanceName}
onChange={onInstanceNameChange} onChange={val => updateProp("_instanceName", val)}
/> />
{/if} {/if}
{#if settings && settings.length > 0} {#if settings && settings.length > 0}
{#each settings as setting (`${componentInstance._id}-${setting.key}`)} {#each settings as setting (`${componentInstance._id}-${setting.key}`)}
{#if canRenderControl(setting)} {#if canRenderControl(setting)}
@ -147,8 +128,11 @@
value={componentInstance[setting.key] ?? value={componentInstance[setting.key] ??
componentInstance[setting.key]?.defaultValue} componentInstance[setting.key]?.defaultValue}
{componentInstance} {componentInstance}
onChange={val => onChange(setting.key, val)} onChange={val => updateProp(setting.key, val)}
props={{ options: setting.options, placeholder: setting.placeholder }} props={{
options: setting.options,
placeholder: setting.placeholder,
}}
/> />
{/if} {/if}
{/each} {/each}
@ -160,39 +144,11 @@
{@html componentDefinition?.info} {@html componentDefinition?.info}
</div> </div>
{/if} {/if}
</DetailSummary>
{#if componentDefinition?.component?.endsWith("/fieldgroup")}
<div class="buttonWrapper">
<Button secondary wide on:click={() => confirmResetFieldsDialog?.show()}>
Update Form Fields
</Button>
</div>
{/if}
</div>
<ConfirmDialog
bind:this={confirmResetFieldsDialog}
body={`All components inside this group will be deleted and replaced with fields to match the schema. Are you sure you want to update this Field Group?`}
okText="Update"
onOk={resetFormFields}
title="Confirm Form Field Update"
/>
<style> <style>
.settings-view-container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
gap: var(--spacing-s);
}
.text { .text {
font-size: var(--spectrum-global-dimension-font-size-75); font-size: var(--spectrum-global-dimension-font-size-75);
margin-top: var(--spacing-m);
color: var(--grey-6); color: var(--grey-6);
} }
.buttonWrapper {
margin-top: 10px;
display: flex;
flex-direction: column;
}
</style> </style>

View File

@ -21,7 +21,7 @@
const setUpChart = provider => { const setUpChart = provider => {
const allCols = [labelColumn, ...(valueColumns || [null])] const allCols = [labelColumn, ...(valueColumns || [null])]
if (!provider || allCols.find(x => x == null)) { if (!provider || !provider.rows?.length || allCols.find(x => x == null)) {
return null return null
} }

View File

@ -21,7 +21,7 @@
// Fetch data on mount // Fetch data on mount
const setUpChart = provider => { const setUpChart = provider => {
const allCols = [dateColumn, openColumn, highColumn, lowColumn, closeColumn] const allCols = [dateColumn, openColumn, highColumn, lowColumn, closeColumn]
if (!provider || allCols.find(x => x == null)) { if (!provider || !provider.rows?.length || allCols.find(x => x == null)) {
return null return null
} }

View File

@ -28,7 +28,7 @@
// Fetch data on mount // Fetch data on mount
const setUpChart = provider => { const setUpChart = provider => {
const allCols = [labelColumn, ...(valueColumns || [null])] const allCols = [labelColumn, ...(valueColumns || [null])]
if (!provider || allCols.find(x => x == null)) { if (!provider || !provider.rows?.length || allCols.find(x => x == null)) {
return null return null
} }

View File

@ -18,7 +18,7 @@
// Fetch data on mount // Fetch data on mount
const setUpChart = provider => { const setUpChart = provider => {
if (!provider || !labelColumn || !valueColumn) { if (!provider || !provider.rows?.length || !labelColumn || !valueColumn) {
return null return null
} }