Browse Source

Merge pull request #4608 from Budibase/table-cell-config

Table V2
pull/4681/head
Andrew Kingston 4 years ago
committed by GitHub
parent
commit
cd3fd172cc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      packages/bbui/package.json
  2. 5
      packages/bbui/src/ColorPicker/ColorPicker.svelte
  3. 21
      packages/bbui/src/Drawer/Drawer.svelte
  4. 15
      packages/bbui/src/Table/AttachmentRenderer.svelte
  5. 4
      packages/bbui/src/Table/BoldRenderer.svelte
  6. 32
      packages/bbui/src/Table/CellRenderer.svelte
  7. 9
      packages/bbui/src/Table/CodeRenderer.svelte
  8. 4
      packages/bbui/src/Table/DateTimeRenderer.svelte
  9. 8
      packages/bbui/src/Table/InternalRenderer.svelte
  10. 3
      packages/bbui/src/Table/StringRenderer.svelte
  11. 586
      packages/bbui/src/Table/Table.svelte
  12. 935
      packages/bbui/yarn.lock
  13. 18
      packages/builder/cypress/integration/createTable.spec.js
  14. 202
      packages/builder/cypress/integration/createUserAndRoles.spec.js
  15. 401
      packages/builder/cypress/integration/datasources/mySql.spec.js
  16. 416
      packages/builder/cypress/integration/datasources/oracle.spec.js
  17. 516
      packages/builder/cypress/integration/datasources/postgreSql.spec.js
  18. 2
      packages/builder/src/components/backend/DataTable/DataTable.svelte
  19. 2
      packages/builder/src/components/backend/DataTable/Table.svelte
  20. 66
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/ColumnEditor/CellDrawer.svelte
  21. 34
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/ColumnEditor/CellEditor.svelte
  22. 57
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/ColumnEditor/ColumnDrawer.svelte
  23. 29
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/ColumnEditor/ColumnEditor.svelte
  24. 13
      packages/client/manifest.json
  25. 2
      packages/client/src/components/app/blocks/TableBlock.svelte
  26. 13
      packages/client/src/components/app/table/Table.svelte

1
packages/bbui/package.json

@ -38,6 +38,7 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1", "@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "^1.0.72-alpha.0",
"@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2", "@spectrum-css/avatar": "^3.0.2",

5
packages/bbui/src/ColorPicker/ColorPicker.svelte

@ -10,6 +10,7 @@
export let value export let value
export let size = "M" export let size = "M"
export let spectrumTheme export let spectrumTheme
export let alignRight = false
let open = false let open = false
@ -133,6 +134,7 @@
use:clickOutside={() => (open = false)} use:clickOutside={() => (open = false)}
transition:fly={{ y: -20, duration: 200 }} transition:fly={{ y: -20, duration: 200 }}
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open" class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
class:spectrum-Popover--align-right={alignRight}
> >
{#each categories as category} {#each categories as category}
<div class="category"> <div class="category">
@ -250,6 +252,9 @@
align-items: stretch; align-items: stretch;
gap: var(--spacing-xl); gap: var(--spacing-xl);
} }
.spectrum-Popover--align-right {
right: 0;
}
.colors { .colors {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;

21
packages/bbui/src/Drawer/Drawer.svelte

@ -1,5 +1,4 @@
<script> <script>
import { slide } from "svelte/transition"
import Portal from "svelte-portal" import Portal from "svelte-portal"
import Button from "../Button/Button.svelte" import Button from "../Button/Button.svelte"
import Body from "../Typography/Body.svelte" import Body from "../Typography/Body.svelte"
@ -7,7 +6,9 @@
export let title export let title
export let fillWidth export let fillWidth
let visible = false let visible = false
export function show() { export function show() {
if (visible) { if (visible) {
return return
@ -21,11 +22,27 @@
} }
visible = false visible = false
} }
const easeInOutQuad = x => {
return x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2
}
// Use a custom svelte transition here because the built-in slide
// transition has a horrible overshoot
const slide = () => {
return {
duration: 360,
css: t => {
const translation = 100 - Math.round(easeInOutQuad(t) * 100)
return `transform: translateY(${translation}%);`
},
}
}
</script> </script>
{#if visible} {#if visible}
<Portal> <Portal>
<section class:fillWidth class="drawer" transition:slide> <section class:fillWidth class="drawer" transition:slide|local>
<header> <header>
<div class="text"> <div class="text">
<Heading size="XS">{title}</Heading> <Heading size="XS">{title}</Heading>

15
packages/bbui/src/Table/AttachmentRenderer.svelte

@ -17,14 +17,16 @@
{#each attachments as attachment} {#each attachments as attachment}
{#if isImage(attachment.extension)} {#if isImage(attachment.extension)}
<Link quiet target="_blank" href={attachment.url}> <Link quiet target="_blank" href={attachment.url}>
<img src={attachment.url} alt={attachment.extension} /> <div class="center">
<img src={attachment.url} alt={attachment.extension} />
</div>
</Link> </Link>
{:else} {:else}
<Tooltip text={attachment.name} direction="right"> <Tooltip text={attachment.name} direction="right">
<div class="file"> <div class="file">
<Link quiet target="_blank" href={attachment.url} <Link quiet target="_blank" href={attachment.url}>
>{attachment.extension}</Link {attachment.extension}
> </Link>
</div> </div>
</Tooltip> </Tooltip>
{/if} {/if}
@ -38,12 +40,15 @@
height: 32px; height: 32px;
max-width: 64px; max-width: 64px;
} }
.center,
.file { .file {
height: 32px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
}
.file {
height: 32px;
padding: 0 8px; padding: 0 8px;
color: var(--spectrum-global-color-gray-800); color: var(--spectrum-global-color-gray-800);
border: 1px solid var(--spectrum-global-color-gray-300); border: 1px solid var(--spectrum-global-color-gray-300);

4
packages/bbui/src/Table/BoldRenderer.svelte

@ -7,5 +7,9 @@
<style> <style>
.bold { .bold {
font-weight: bold; font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: var(--max-cell-width);
} }
</style> </style>

32
packages/bbui/src/Table/CellRenderer.svelte

@ -6,6 +6,7 @@
import AttachmentRenderer from "./AttachmentRenderer.svelte" import AttachmentRenderer from "./AttachmentRenderer.svelte"
import ArrayRenderer from "./ArrayRenderer.svelte" import ArrayRenderer from "./ArrayRenderer.svelte"
import InternalRenderer from "./InternalRenderer.svelte" import InternalRenderer from "./InternalRenderer.svelte"
import { processStringSync } from "@budibase/string-templates"
export let row export let row
export let schema export let schema
@ -28,10 +29,33 @@
$: type = schema?.type ?? "string" $: type = schema?.type ?? "string"
$: customRenderer = customRenderers?.find(x => x.column === schema?.name) $: customRenderer = customRenderers?.find(x => x.column === schema?.name)
$: renderer = customRenderer?.component ?? typeMap[type] ?? StringRenderer $: renderer = customRenderer?.component ?? typeMap[type] ?? StringRenderer
$: width = schema?.width || "150px"
$: cellValue = getCellValue(value, schema.template)
const getCellValue = (value, template) => {
if (!template) {
return value
}
return processStringSync(template, { value })
}
</script> </script>
{#if renderer && (customRenderer || (value != null && value !== ""))} {#if renderer && (customRenderer || (cellValue != null && cellValue !== ""))}
<svelte:component this={renderer} {row} {schema} {value} on:clickrelationship> <div style="--max-cell-width: {schema.width ? 'none' : '200px'};">
<slot /> <svelte:component
</svelte:component> this={renderer}
{row}
{schema}
value={cellValue}
on:clickrelationship
>
<slot />
</svelte:component>
</div>
{/if} {/if}
<style>
div {
display: contents;
}
</style>

9
packages/bbui/src/Table/CodeRenderer.svelte

@ -3,3 +3,12 @@
</script> </script>
<code>{value}</code> <code>{value}</code>
<style>
code {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: var(--max-cell-width);
}
</style>

4
packages/bbui/src/Table/DateTimeRenderer.svelte

@ -17,6 +17,8 @@
<style> <style>
div { div {
width: 200px; overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
</style> </style>

8
packages/bbui/src/Table/InternalRenderer.svelte

@ -43,11 +43,3 @@
<div on:click|stopPropagation={onClick}> <div on:click|stopPropagation={onClick}>
<Icon size="S" name="Copy" /> <Icon size="S" name="Copy" />
</div> </div>
<style>
div {
overflow: hidden;
text-overflow: ellipsis;
width: 150px;
}
</style>

3
packages/bbui/src/Table/StringRenderer.svelte

@ -8,6 +8,7 @@
div { div {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
width: 150px; white-space: nowrap;
max-width: var(--max-cell-width);
} }
</style> </style>

586
packages/bbui/src/Table/Table.svelte

@ -4,6 +4,7 @@
import CellRenderer from "./CellRenderer.svelte" import CellRenderer from "./CellRenderer.svelte"
import SelectEditRenderer from "./SelectEditRenderer.svelte" import SelectEditRenderer from "./SelectEditRenderer.svelte"
import { cloneDeep, deepGet } from "../helpers" import { cloneDeep, deepGet } from "../helpers"
import ProgressCircle from "../ProgressCircle/ProgressCircle.svelte"
/** /**
* The expected schema is our normal couch schemas for our tables. * The expected schema is our normal couch schemas for our tables.
@ -14,6 +15,11 @@
* sortable: Set to false to disable sorting data by a certain column * sortable: Set to false to disable sorting data by a certain column
* editable: Set to false to disable editing a certain column if the * editable: Set to false to disable editing a certain column if the
* allowEditColumns prop is true * allowEditColumns prop is true
* width: the width of the column
* align: the alignment of the column
* template: a HBS or JS binding to use as the value
* background: the background color
* color: the text color
*/ */
export let data = [] export let data = []
export let schema = {} export let schema = {}
@ -28,13 +34,14 @@
export let editColumnTitle = "Edit" export let editColumnTitle = "Edit"
export let customRenderers = [] export let customRenderers = []
export let disableSorting = false export let disableSorting = false
export let autoSortColumns = true
export let compact = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
// Config // Config
const rowHeight = 55
const headerHeight = 36 const headerHeight = 36
const rowPreload = 5 $: rowHeight = compact ? 46 : 55
// Sorting state // Sorting state
let sortColumn let sortColumn
@ -45,32 +52,20 @@
let loaded = false let loaded = false
$: schema = fixSchema(schema) $: schema = fixSchema(schema)
$: if (!loading) loaded = true $: if (!loading) loaded = true
$: rows = data ?? [] $: fields = getFields(schema, showAutoColumns, autoSortColumns)
$: visibleRowCount = getVisibleRowCount(loaded, height, rows.length, rowCount) $: rows = fields?.length ? data || [] : []
$: contentStyle = getContentStyle(visibleRowCount, rowCount) $: visibleRowCount = getVisibleRowCount(
loaded,
height,
rows.length,
rowCount,
rowHeight
)
$: contentStyle = getContentStyle(visibleRowCount, rowCount, rowHeight)
$: sortedRows = sortRows(rows, sortColumn, sortOrder) $: sortedRows = sortRows(rows, sortColumn, sortOrder)
$: fields = getFields(schema, showAutoColumns) $: gridStyle = getGridStyle(fields, schema, showEditColumn)
$: showEditColumn = allowEditRows || allowSelectRows $: showEditColumn = allowEditRows || allowSelectRows
$: cellStyles = computeCellStyles(schema)
// Scrolling state
let timeout
let nextScrollTop = 0
let scrollTop = 0
$: firstVisibleRow = calculateFirstVisibleRow(scrollTop)
$: lastVisibleRow = calculateLastVisibleRow(
firstVisibleRow,
visibleRowCount,
rows.length
)
// Reset state when data changes
$: rows.length, reset()
const reset = () => {
nextScrollTop = 0
scrollTop = 0
clearTimeout(timeout)
timeout = null
}
const fixSchema = schema => { const fixSchema = schema => {
let fixedSchema = {} let fixedSchema = {}
@ -90,7 +85,7 @@
return fixedSchema return fixedSchema
} }
const getVisibleRowCount = (loaded, height, allRows, rowCount) => { const getVisibleRowCount = (loaded, height, allRows, rowCount, rowHeight) => {
if (!loaded) { if (!loaded) {
return rowCount || 0 return rowCount || 0
} }
@ -100,11 +95,28 @@
return Math.min(allRows, Math.ceil(height / rowHeight)) return Math.min(allRows, Math.ceil(height / rowHeight))
} }
const getContentStyle = (visibleRows, rowCount) => { const getContentStyle = (visibleRows, rowCount, rowHeight) => {
if (!rowCount || !visibleRows) { if (!rowCount || !visibleRows) {
return "" return ""
} }
return `height: ${headerHeight + visibleRows * (rowHeight + 1)}px;` return `height: ${headerHeight + visibleRows * rowHeight}px;`
}
const getGridStyle = (fields, schema, showEditColumn) => {
let style = "grid-template-columns:"
if (showEditColumn) {
style += " auto"
}
fields?.forEach(field => {
const fieldSchema = schema[field]
if (fieldSchema.width) {
style += ` ${fieldSchema.width}`
} else {
style += " minmax(auto, 1fr)"
}
})
style += ";"
return style
} }
const sortRows = (rows, sortColumn, sortOrder) => { const sortRows = (rows, sortColumn, sortOrder) => {
@ -143,14 +155,14 @@
return name || "" return name || ""
} }
const getFields = (schema, showAutoColumns) => { const getFields = (schema, showAutoColumns, autoSortColumns) => {
let columns = [] let columns = []
let autoColumns = [] let autoColumns = []
Object.entries(schema || {}).forEach(([field, fieldSchema]) => { Object.entries(schema || {}).forEach(([field, fieldSchema]) => {
if (!field || !fieldSchema) { if (!field || !fieldSchema) {
return return
} }
if (!fieldSchema?.autocolumn) { if (!autoSortColumns || !fieldSchema?.autocolumn) {
columns.push(fieldSchema) columns.push(fieldSchema)
} else if (showAutoColumns) { } else if (showAutoColumns) {
autoColumns.push(fieldSchema) autoColumns.push(fieldSchema)
@ -171,28 +183,6 @@
.map(column => column.name) .map(column => column.name)
} }
const onScroll = event => {
nextScrollTop = event.target.scrollTop
if (timeout) {
return
}
timeout = setTimeout(() => {
scrollTop = nextScrollTop
timeout = null
}, 50)
}
const calculateFirstVisibleRow = scrollTop => {
return Math.max(Math.floor(scrollTop / (rowHeight + 1)) - rowPreload, 0)
}
const calculateLastVisibleRow = (firstRow, visibleRowCount, allRowCount) => {
if (visibleRowCount === 0) {
return -1
}
return Math.min(firstRow + visibleRowCount + 2 * rowPreload, allRowCount)
}
const editColumn = (e, field) => { const editColumn = (e, field) => {
e.stopPropagation() e.stopPropagation()
dispatch("editcolumn", field) dispatch("editcolumn", field)
@ -213,170 +203,233 @@
selectedRows = [...selectedRows, row] selectedRows = [...selectedRows, row]
} }
} }
const computeCellStyles = schema => {
let styles = {}
Object.keys(schema || {}).forEach(field => {
styles[field] = ""
if (schema[field].color) {
styles[field] += `color: ${schema[field].color};`
}
if (schema[field].background) {
styles[field] += `background-color: ${schema[field].background};`
}
if (schema[field].align === "Center") {
styles[field] += "justify-content: center; text-align: center;"
}
if (schema[field].align === "Right") {
styles[field] += "justify-content: flex-end; text-align: right;"
}
})
return styles
}
</script> </script>
<div class="wrapper" bind:offsetHeight={height}> <div
class="wrapper"
class:wrapper--quiet={quiet}
class:wrapper--compact={compact}
bind:offsetHeight={height}
style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`}
>
{#if !loaded} {#if !loaded}
<div class="loading" style={contentStyle} /> <div class="loading" style={contentStyle}>
<ProgressCircle />
</div>
{:else} {:else}
<div <div class="spectrum-Table" style={`${contentStyle}${gridStyle}`}>
on:scroll={onScroll} {#if fields.length}
class:quiet <div class="spectrum-Table-head">
style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`} {#if showEditColumn}
class="container" <div
> class="spectrum-Table-headCell spectrum-Table-headCell--divider spectrum-Table-headCell--edit"
<div style={contentStyle}> >
<table class="spectrum-Table" class:spectrum-Table--quiet={quiet}> {editColumnTitle || ""}
{#if fields.length} </div>
<thead class="spectrum-Table-head">
<tr>
{#if showEditColumn}
<th class="spectrum-Table-headCell">
<div class="spectrum-Table-headCell-content">
{editColumnTitle || ""}
</div>
</th>
{/if}
{#each fields as field}
<th
class="spectrum-Table-headCell"
class:is-sortable={schema[field].sortable !== false}
class:is-sorted-desc={sortColumn === field &&
sortOrder === "Descending"}
class:is-sorted-asc={sortColumn === field &&
sortOrder === "Ascending"}
on:click={() => sortBy(schema[field])}
>
<div class="spectrum-Table-headCell-content">
<div class="title">{getDisplayName(schema[field])}</div>
{#if schema[field]?.autocolumn}
<svg
class="spectrum-Icon spectrum-Table-autoIcon"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-MagicWand" />
</svg>
{/if}
{#if sortColumn === field}
<svg
class="spectrum-Icon spectrum-UIIcon-ArrowDown100 spectrum-Table-sortedIcon"
focusable="false"
aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Arrow100" />
</svg>
{/if}
{#if allowEditColumns && schema[field]?.editable !== false}
<svg
class="spectrum-Icon spectrum-Table-editIcon"
focusable="false"
on:click={e => editColumn(e, field)}
>
<use xlink:href="#spectrum-icon-18-Edit" />
</svg>
{/if}
</div>
</th>
{/each}
</tr>
</thead>
{/if} {/if}
<tbody class="spectrum-Table-body"> {#each fields as field}
{#if sortedRows?.length && fields.length} <div
{#each sortedRows as row, idx} class="spectrum-Table-headCell"
<tr class:spectrum-Table-headCell--alignCenter={schema[field]
on:click={() => dispatch("click", row)} .align === "Center"}
on:click={() => toggleSelectRow(row)} class:spectrum-Table-headCell--alignRight={schema[field].align ===
class="spectrum-Table-row" "Right"}
class:hidden={idx < firstVisibleRow || idx > lastVisibleRow} class:is-sortable={schema[field].sortable !== false}
class:is-sorted-desc={sortColumn === field &&
sortOrder === "Descending"}
class:is-sorted-asc={sortColumn === field &&
sortOrder === "Ascending"}
on:click={() => sortBy(schema[field])}
>
<div class="title">{getDisplayName(schema[field])}</div>
{#if schema[field]?.autocolumn}
<svg
class="spectrum-Icon spectrum-Table-autoIcon"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-MagicWand" />
</svg>
{/if}
{#if sortColumn === field}
<svg
class="spectrum-Icon spectrum-UIIcon-ArrowDown100 spectrum-Table-sortedIcon"
focusable="false"
aria-hidden="true"
> >
{#if idx >= firstVisibleRow && idx <= lastVisibleRow} <use xlink:href="#spectrum-css-icon-Arrow100" />
{#if showEditColumn} </svg>
<td {/if}
class="spectrum-Table-cell spectrum-Table-cell--divider" {#if allowEditColumns && schema[field]?.editable !== false}
> <svg
<div class="spectrum-Table-cell-content"> class="spectrum-Icon spectrum-Table-editIcon"
<SelectEditRenderer focusable="false"
data={row} on:click={e => editColumn(e, field)}
selected={selectedRows.includes(row)} >
onToggleSelection={() => toggleSelectRow(row)} <use xlink:href="#spectrum-icon-18-Edit" />
onEdit={e => editRow(e, row)} </svg>
{allowSelectRows} {/if}
{allowEditRows} </div>
/> {/each}
</div> </div>
</td> {/if}
{/if} {#if sortedRows?.length}
{#each fields as field} {#each sortedRows as row, idx}
<td <div
class="spectrum-Table-cell" class="spectrum-Table-row"
class:spectrum-Table-cell--divider={!!schema[field] on:click={() => dispatch("click", row)}
.divider} on:click={() => toggleSelectRow(row)}
> >
<div class="spectrum-Table-cell-content"> {#if showEditColumn}
<CellRenderer <div
{customRenderers} class="spectrum-Table-cell spectrum-Table-cell--divider spectrum-Table-cell--edit"
{row} >
schema={schema[field]} <SelectEditRenderer
value={deepGet(row, field)} data={row}
on:clickrelationship selected={selectedRows.includes(row)}
> onToggleSelection={() => toggleSelectRow(row)}
<slot /> onEdit={e => editRow(e, row)}
</CellRenderer> {allowSelectRows}
</div> {allowEditRows}
</td> />
{/each} </div>
{/if}
</tr>
{/each}
{:else}
<tr class="placeholder-row">
{#if showEditColumn}
<td class="placeholder-offset" />
{/if}
{#each fields as field}
<td />
{/each}
<div class="placeholder" class:has-fields={fields.length > 0}>
<div class="placeholder-content">
<svg
class="spectrum-Icon spectrum-Icon--sizeXXL"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-Table" />
</svg>
<div>No rows found</div>
</div>
</div>
</tr>
{/if} {/if}
</tbody> {#each fields as field}
</table> <div
</div> class="spectrum-Table-cell"
class:spectrum-Table-cell--divider={!!schema[field].divider}
style={cellStyles[field]}
>
<CellRenderer
{customRenderers}
{row}
schema={schema[field]}
value={deepGet(row, field)}
on:clickrelationship
>
<slot />
</CellRenderer>
</div>
{/each}
</div>
{/each}
{:else}
<div class="placeholder" class:placeholder--no-fields={!fields?.length}>
<div class="placeholder-content">
<svg class="spectrum-Icon spectrum-Icon--sizeXXL" focusable="false">
<use xlink:href="#spectrum-icon-18-Table" />
</svg>
<div>No rows found</div>
</div>
</div>
{/if}
</div> </div>
{/if} {/if}
</div> </div>
<style> <style>
/* Wrapper */
.wrapper { .wrapper {
background-color: var(--spectrum-alias-background-color-secondary);
overflow: hidden;
position: relative; position: relative;
z-index: 0; z-index: 0;
--table-bg: var(--spectrum-global-color-gray-50);
--table-border: 1px solid var(--spectrum-alias-border-color-mid);
--cell-padding: var(--spectrum-global-dimension-size-250);
} }
.wrapper--quiet {
.container { --table-bg: var(--spectrum-alias-background-color-transparent);
height: 100%;
position: relative;
overflow: auto;
} }
.container.quiet { .wrapper--compact {
border: none; --cell-padding: var(--spectrum-global-dimension-size-150);
} }
table {
/* Loading */
.loading {
display: grid;
place-items: center;
min-height: 100px;
}
/* Table */
.spectrum-Table {
width: 100%; width: 100%;
border-radius: 0;
display: grid;
overflow: auto;
} }
/* Header */
.spectrum-Table-head {
display: contents;
}
.spectrum-Table-head > :first-child {
border-left: 1px solid transparent;
padding-left: var(--cell-padding);
}
.spectrum-Table-head > :last-child {
border-right: 1px solid transparent;
padding-right: var(--cell-padding);
}
.spectrum-Table-headCell {
height: var(--header-height);
position: sticky;
top: 0;
text-overflow: ellipsis;
white-space: nowrap;
background-color: var(--spectrum-alias-background-color-secondary);
z-index: 2;
border-bottom: var(--table-border);
padding: 0 calc(var(--cell-padding) / 1.33);
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
user-select: none;
}
.spectrum-Table-headCell--alignCenter {
justify-content: center;
}
.spectrum-Table-headCell--alignRight {
justify-content: flex-end;
}
.spectrum-Table-headCell--divider {
padding-right: var(--cell-padding);
}
.spectrum-Table-headCell--divider + .spectrum-Table-headCell {
padding-left: var(--cell-padding);
}
.spectrum-Table-headCell--edit {
position: sticky;
left: 0;
z-index: 3;
}
.spectrum-Table-headCell .title {
overflow: hidden;
text-overflow: ellipsis;
}
.spectrum-Table-headCell:hover .spectrum-Table-editIcon {
opacity: 1;
transition: opacity 0.2s ease;
}
.spectrum-Table-headCell .spectrum-Icon { .spectrum-Table-headCell .spectrum-Icon {
pointer-events: all; pointer-events: all;
margin-left: var( margin-left: var(
@ -392,63 +445,93 @@
.spectrum-Table-editIcon { .spectrum-Table-editIcon {
opacity: 0; opacity: 0;
} }
.spectrum-Table-headCell:hover .spectrum-Table-editIcon {
opacity: 1;
transition: opacity 0.2s ease;
}
th { /* Table rows */
vertical-align: middle; .spectrum-Table-row {
height: var(--header-height); display: contents;
position: sticky; }
top: 0; .spectrum-Table-row:hover .spectrum-Table-cell {
z-index: 2; /*background-color: var(--hover-bg) !important;*/
background-color: var(--spectrum-alias-background-color-secondary); }
border-bottom: 1px solid .spectrum-Table-row:hover .spectrum-Table-cell:after {
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid)); background-color: var(--spectrum-alias-highlight-hover);
}
.wrapper--quiet .spectrum-Table-row {
border-left: none;
border-right: none;
}
.spectrum-Table-row > :first-child {
border-left: var(--table-border);
padding-left: var(--cell-padding);
}
.spectrum-Table-row > :last-child {
border-right: var(--table-border);
padding-right: var(--cell-padding);
} }
.spectrum-Table-headCell-content {
/* Table cells */
.spectrum-Table-cell {
flex: 1 1 auto;
padding: 0 calc(var(--cell-padding) / 1.33);
border-top: none;
border-bottom: none;
border-radius: 0;
text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
height: var(--row-height);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
user-select: none; gap: 4px;
} border-bottom: 1px solid var(--spectrum-alias-border-color-mid);
.spectrum-Table-headCell-content .title { background-color: var(--table-bg);
overflow: hidden; z-index: 1;
text-overflow: ellipsis;
} }
.spectrum-Table-cell--divider {
.placeholder-row { padding-right: var(--cell-padding);
position: relative;
height: 150px;
} }
.placeholder-row td { .spectrum-Table-cell--divider + .spectrum-Table-cell {
border-top: none !important; padding-left: var(--cell-padding);
border-bottom: none !important;
} }
.placeholder-offset { .spectrum-Table-cell--edit {
width: 1px; position: sticky;
left: 0;
z-index: 2;
} }
.placeholder { .spectrum-Table-cell:after {
top: 0; content: "";
position: absolute;
width: 100%;
height: 100%; height: 100%;
background-color: transparent;
top: 0;
left: 0; left: 0;
width: 100%; pointer-events: none;
position: absolute; transition: background-color
var(--spectrum-global-animation-duration-100, 0.13s) ease-in-out;
}
/* Placeholder */
.placeholder {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
border: var(--table-border);
border-top: none;
grid-column: 1 / -1;
background-color: var(--table-bg);
} }
.placeholder.has-fields { .placeholder--no-fields {
top: var(--header-height); border-top: var(--table-border);
height: calc(100% - var(--header-height)); }
.wrapper--quiet .placeholder {
border-left: none;
border-right: none;
} }
.placeholder-content { .placeholder-content {
padding: 20px; padding: 40px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
@ -466,41 +549,4 @@
); );
text-align: center; text-align: center;
} }
tbody {
z-index: 1;
}
tbody tr {
height: var(--row-height);
}
tbody tr.hidden {
height: calc(var(--row-height) + 1px);
}
td {
padding-top: 0;
padding-bottom: 0;
border-bottom: none;
border-top: 1px solid
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
border-radius: 0;
}
tr:first-child td {
border-top: none;
}
tr:last-child td {
border-bottom: 1px solid
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
}
td.spectrum-Table-cell--divider {
width: 1px;
}
.spectrum-Table-cell-content {
height: var(--row-height);
white-space: nowrap;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: 4px;
}
</style> </style>

935
packages/bbui/yarn.lock

File diff suppressed because it is too large

18
packages/builder/cypress/integration/createTable.spec.js

@ -1,6 +1,6 @@
import filterTests from "../support/filterTests" import filterTests from "../support/filterTests"
filterTests(['smoke', 'all'], () => { filterTests(["smoke", "all"], () => {
context("Create a Table", () => { context("Create a Table", () => {
before(() => { before(() => {
cy.login() cy.login()
@ -33,7 +33,7 @@ filterTests(['smoke', 'all'], () => {
cy.contains("Save Column").click() cy.contains("Save Column").click()
cy.contains("nameupdated ").should("contain", "nameupdated") cy.contains("nameupdated ").should("contain", "nameupdated")
}) })
it("edits a row", () => { it("edits a row", () => {
cy.contains("button", "Edit").click({ force: true }) cy.contains("button", "Edit").click({ force: true })
cy.wait(1000) cy.wait(1000)
@ -42,20 +42,20 @@ filterTests(['smoke', 'all'], () => {
cy.contains("Save").click() cy.contains("Save").click()
cy.contains("Updated").should("have.text", "Updated") cy.contains("Updated").should("have.text", "Updated")
}) })
it("deletes a row", () => { it("deletes a row", () => {
cy.get(".spectrum-Checkbox-input").check({ force: true }) cy.get(".spectrum-Checkbox-input").check({ force: true })
cy.contains("Delete 1 row(s)").click() cy.contains("Delete 1 row(s)").click()
cy.get(".spectrum-Modal").contains("Delete").click() cy.get(".spectrum-Modal").contains("Delete").click()
cy.contains("RoverUpdated").should("not.exist") cy.contains("RoverUpdated").should("not.exist")
}) })
if (Cypress.env("TEST_ENV")) { if (Cypress.env("TEST_ENV")) {
// No Pagination in CI - Test env only for the next two tests // No Pagination in CI - Test env only for the next two tests
it("Adds 15 rows and checks pagination", () => { it("Adds 15 rows and checks pagination", () => {
// 10 rows per page, 15 rows should create 2 pages within table // 10 rows per page, 15 rows should create 2 pages within table
const totalRows = 16 const totalRows = 16
for (let i = 1; i < totalRows; i++){ for (let i = 1; i < totalRows; i++) {
cy.addRow([i]) cy.addRow([i])
} }
cy.wait(1000) cy.wait(1000)
@ -66,19 +66,19 @@ filterTests(['smoke', 'all'], () => {
cy.get(".spectrum-Body--secondary").contains("Page 2") cy.get(".spectrum-Body--secondary").contains("Page 2")
}) })
}) })
it("Deletes rows and checks pagination", () => { it("Deletes rows and checks pagination", () => {
// Delete rows, removing second page of rows from table // Delete rows, removing second page of rows from table
const deleteRows = 5 const deleteRows = 5
cy.get(".spectrum-Checkbox-input").check({ force: true }) cy.get(".spectrum-Checkbox-input").check({ force: true })
cy.get(".spectrum-Table-body") cy.get(".spectrum-Table")
cy.contains("Delete 5 row(s)").click() cy.contains("Delete 5 row(s)").click()
cy.get(".spectrum-Modal").contains("Delete").click() cy.get(".spectrum-Modal").contains("Delete").click()
cy.wait(1000) cy.wait(1000)
// Confirm table only has one page // Confirm table only has one page
cy.get(".spectrum-Pagination").within(() => { cy.get(".spectrum-Pagination").within(() => {
cy.get(".spectrum-ActionButton").eq(1).should('not.be.enabled') cy.get(".spectrum-ActionButton").eq(1).should("not.be.enabled")
}) })
}) })
} }

202
packages/builder/cypress/integration/createUserAndRoles.spec.js

@ -1,7 +1,6 @@
import filterTests from "../support/filterTests" import filterTests from "../support/filterTests"
filterTests(["smoke", "all"], () => {
filterTests(['smoke', 'all'], () => {
context("Create a User and Assign Roles", () => { context("Create a User and Assign Roles", () => {
before(() => { before(() => {
cy.login() cy.login()
@ -9,32 +8,32 @@ filterTests(['smoke', 'all'], () => {
it("should create a user", () => { it("should create a user", () => {
cy.createUser("bbuser@test.com") cy.createUser("bbuser@test.com")
cy.get(".spectrum-Table-body").should('contain', 'bbuser') cy.get(".spectrum-Table").should("contain", "bbuser")
}) })
it("should confirm there is No Access for a New User", () => { it("should confirm there is No Access for a New User", () => {
// Click into the user // Click into the user
cy.contains("bbuser").click() cy.contains("bbuser").click()
cy.wait(500) cy.wait(500)
// Get No Access table - Confirm it has apps in it // Get No Access table - Confirm it has apps in it
cy.get(".spectrum-Table").eq(1).should('not.contain', 'No rows found') cy.get(".spectrum-Table").eq(1).should("not.contain", "No rows found")
// Get Configure Roles table - Confirm it has no apps // Get Configure Roles table - Confirm it has no apps
cy.get(".spectrum-Table").eq(0).contains('No rows found') cy.get(".spectrum-Table").eq(0).contains("No rows found")
}) })
it("should assign role types", () => { it("should assign role types", () => {
// 3 apps minimum required - to assign an app to each role type // 3 apps minimum required - to assign an app to each role type
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
.its("body") .its("body")
.then(val => { .then(val => {
if (val.length < 3) { if (val.length < 3) {
for (let i = 1; i < 3; i++) { for (let i = 1; i < 3; i++) {
const uuid = () => Cypress._.random(0, 1e6) const uuid = () => Cypress._.random(0, 1e6)
const name = uuid() const name = uuid()
cy.createApp(name) cy.createApp(name)
}
} }
} })
})
// Navigate back to the user // Navigate back to the user
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.wait(500) cy.wait(500)
@ -43,90 +42,139 @@ filterTests(['smoke', 'all'], () => {
cy.get(".spectrum-Table").contains("bbuser").click() cy.get(".spectrum-Table").contains("bbuser").click()
cy.wait(1000) cy.wait(1000)
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
cy.get(".spectrum-Table-body").eq(1).find('tr').eq(0).click() cy.get(".spectrum-Table")
.eq(1)
.find(".spectrum-Table-row")
.eq(0)
.find(".spectrum-Table-cell")
.eq(0)
.click()
cy.wait(500) cy.wait(500)
cy.get(".spectrum-Dialog-grid").contains("Choose an option").click().then(() => { cy.get(".spectrum-Dialog-grid")
cy.wait(1000) .contains("Choose an option")
if (i == 0) { .click()
cy.get(".spectrum-Popover").contains("Admin").click() .then(() => {
} cy.wait(1000)
if (i == 1) { if (i == 0) {
cy.get(".spectrum-Popover").contains("Power").click() cy.get(".spectrum-Popover").contains("Admin").click()
} }
if (i == 2) { if (i == 1) {
cy.get(".spectrum-Popover").contains("Basic").click() cy.get(".spectrum-Popover").contains("Power").click()
} }
cy.wait(1000) if (i == 2) {
cy.get(".spectrum-Button").contains("Update role").click({ force: true }) cy.get(".spectrum-Popover").contains("Basic").click()
}
cy.wait(1000)
cy.get(".spectrum-Button")
.contains("Update role")
.click({ force: true })
}) })
} }
// Confirm roles exist within Configure roles table // Confirm roles exist within Configure roles table
cy.wait(2000) cy.wait(2000)
cy.get(".spectrum-Table-body").eq(0).within((assginedRoles) => { cy.get(".spectrum-Table")
.eq(0)
.within(assginedRoles => {
expect(assginedRoles).to.contain("Admin") expect(assginedRoles).to.contain("Admin")
expect(assginedRoles).to.contain("Power") expect(assginedRoles).to.contain("Power")
expect(assginedRoles).to.contain("Basic") expect(assginedRoles).to.contain("Basic")
}) })
}) })
it("should unassign role types", () => { it("should unassign role types", () => {
// Set each app within Configure roles table to 'No Access' // Set each app within Configure roles table to 'No Access'
cy.get(".spectrum-Table-body").eq(0).find('tr').its('length').then((len) => { cy.get(".spectrum-Table")
for (let i = 0; i < len; i ++){ .eq(0)
cy.get(".spectrum-Table-body").eq(0).find('tr').eq(0).click().then(() => { .find(".spectrum-Table-row")
cy.get(".spectrum-Picker").eq(1).click({ force: true }) .its("length")
cy.wait(500) .then(len => {
cy.get(".spectrum-Popover").contains("No Access").click() for (let i = 0; i < len; i++) {
}) cy.get(".spectrum-Table")
cy.get(".spectrum-Button").contains("Update role").click({ force: true }) .eq(0)
.find(".spectrum-Table-row")
.eq(0)
.find(".spectrum-Table-cell")
.eq(0)
.click()
.then(() => {
cy.get(".spectrum-Picker").eq(1).click({ force: true })
cy.wait(500)
cy.get(".spectrum-Popover").contains("No Access").click()
})
cy.get(".spectrum-Button")
.contains("Update role")
.click({ force: true })
cy.wait(1000) cy.wait(1000)
} }
}) })
// Confirm Configure roles table no longer has any apps in it // Confirm Configure roles table no longer has any apps in it
cy.get(".spectrum-Table-body").eq(0).contains('No rows found') cy.get(".spectrum-Table").eq(0).contains("No rows found")
}) })
it("should enable Developer access", () => { it("should enable Developer access", () => {
// Enable Developer access // Enable Developer access
cy.get(".field").eq(4).within(() => { cy.get(".field")
cy.get(".spectrum-Switch-input").click({ force: true }) .eq(4)
}) .within(() => {
cy.get(".spectrum-Switch-input").click({ force: true })
})
// No Access table should now be empty // No Access table should now be empty
cy.get(".container").contains("No Access").parent().within(() => { cy.get(".container")
cy.get(".spectrum-Table").contains("No rows found") .contains("No Access")
}) .parent()
.within(() => {
cy.get(".spectrum-Table").contains("No rows found")
})
// Each app within Configure roles should have Admin access // Each app within Configure roles should have Admin access
cy.get(".spectrum-Table-body").eq(0).find('tr').its('length').then((len) => { cy.get(".spectrum-Table")
for (let i = 0; i < len; i++) { .eq(0)
cy.get(".spectrum-Table-body").eq(0).find('tr').eq(i).contains("Admin") .find(".spectrum-Table-row")
cy.wait(500) .its("length")
} .then(len => {
}) for (let i = 0; i < len; i++) {
cy.get(".spectrum-Table")
.eq(0)
.find(".spectrum-Table-row")
.eq(i)
.contains("Admin")
cy.wait(500)
}
})
}) })
it("should disable Developer access", () => { it("should disable Developer access", () => {
// Disable Developer access // Disable Developer access
cy.get(".field").eq(4).within(() => { cy.get(".field")
cy.get(".spectrum-Switch-input").click({ force: true }) .eq(4)
}) .within(() => {
cy.get(".spectrum-Switch-input").click({ force: true })
})
// Configure roles table should now be empty // Configure roles table should now be empty
cy.get(".container").contains("Configure roles").parent().within(() => { cy.get(".container")
cy.get(".spectrum-Table").contains("No rows found") .contains("Configure roles")
}) .parent()
.within(() => {
cy.get(".spectrum-Table").contains("No rows found")
})
}) })
it("should delete a user", () => { it("should delete a user", () => {
// Click Delete user button // Click Delete user button
cy.get(".spectrum-Button").contains("Delete user").click({force: true}).then(() => { cy.get(".spectrum-Button")
// Confirm deletion within modal .contains("Delete user")
cy.wait(500) .click({ force: true })
cy.get(".spectrum-Dialog-grid").within(() => { .then(() => {
cy.get(".spectrum-Button").contains("Delete user").click({force: true}) // Confirm deletion within modal
cy.wait(4000) cy.wait(500)
cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Button")
.contains("Delete user")
.click({ force: true })
cy.wait(4000)
})
}) })
}) cy.get(".spectrum-Table").should("not.have.text", "bbuser")
cy.get(".spectrum-Table-body").should("not.have.text", "bbuser")
}) })
}) })
}) })

401
packages/builder/cypress/integration/datasources/mySql.spec.js

@ -1,191 +1,222 @@
import filterTests from "../../support/filterTests" import filterTests from "../../support/filterTests"
filterTests(['all'], () => { filterTests(["all"], () => {
context("MySQL Datasource Testing", () => { context("MySQL Datasource Testing", () => {
if (Cypress.env("TEST_ENV")) { if (Cypress.env("TEST_ENV")) {
before(() => {
before(() => { cy.login()
cy.login() cy.createTestApp()
cy.createTestApp() })
}) const datasource = "MySQL"
const datasource = "MySQL" const queryName = "Cypress Test Query"
const queryName = "Cypress Test Query" const queryRename = "CT Query Rename"
const queryRename = "CT Query Rename"
it("Should add MySQL data source without configuration", () => {
it("Should add MySQL data source without configuration", () => { // Select MySQL data source
// Select MySQL data source cy.selectExternalDatasource(datasource)
cy.selectExternalDatasource(datasource) // Attempt to fetch tables without applying configuration
// Attempt to fetch tables without applying configuration cy.intercept("**/datasources").as("datasource")
cy.intercept('**/datasources').as('datasource') cy.get(".spectrum-Button")
cy.get(".spectrum-Button") .contains("Save and fetch tables")
.contains("Save and fetch tables") .click({ force: true })
.click({ force: true }) // Intercept Request after button click & apply assertions
// Intercept Request after button click & apply assertions cy.wait("@datasource")
cy.wait("@datasource") cy.get("@datasource")
cy.get("@datasource").its('response.body') .its("response.body")
.should('have.property', 'message', 'connect ECONNREFUSED 127.0.0.1:3306') .should(
cy.get("@datasource").its('response.body') "have.property",
.should('have.property', 'status', 500) "message",
}) "connect ECONNREFUSED 127.0.0.1:3306"
)
it("should add MySQL data source and fetch tables", () => { cy.get("@datasource")
// Add & configure MySQL data source .its("response.body")
cy.selectExternalDatasource(datasource) .should("have.property", "status", 500)
cy.intercept('**/datasources').as('datasource') })
cy.addDatasourceConfig(datasource)
// Check response from datasource after adding configuration it("should add MySQL data source and fetch tables", () => {
cy.wait("@datasource") // Add & configure MySQL data source
cy.get("@datasource").its('response.statusCode') cy.selectExternalDatasource(datasource)
.should('eq', 200) cy.intercept("**/datasources").as("datasource")
// Confirm fetch tables was successful cy.addDatasourceConfig(datasource)
cy.get(".spectrum-Table-body").eq(0) // Check response from datasource after adding configuration
.find('tr') cy.wait("@datasource")
.its('length') cy.get("@datasource").its("response.statusCode").should("eq", 200)
.should('be.gt', 0) // Confirm fetch tables was successful
}) cy.get(".spectrum-Table")
.eq(0)
it("should check table fetching error", () => { .find(".spectrum-Table-row")
// MySQL test data source contains tables without primary keys .its("length")
cy.get(".spectrum-InLineAlert") .should("be.gt", 0)
.should('contain', 'Error fetching tables') })
.and('contain', 'No primary key constraint found')
}) it("should check table fetching error", () => {
// MySQL test data source contains tables without primary keys
it("should define a One relationship type", () => { cy.get(".spectrum-InLineAlert")
// Select relationship type & configure .should("contain", "Error fetching tables")
cy.get(".spectrum-Button").contains("Define relationship").click({ force: true }) .and("contain", "No primary key constraint found")
cy.get(".spectrum-Dialog-grid").within(() => { })
cy.get(".spectrum-Picker").eq(0).click()
cy.get(".spectrum-Popover").contains("One").click() it("should define a One relationship type", () => {
cy.get(".spectrum-Picker").eq(1).click() // Select relationship type & configure
cy.get(".spectrum-Popover").contains("REGIONS").click() cy.get(".spectrum-Button")
cy.get(".spectrum-Picker").eq(2).click() .contains("Define relationship")
cy.get(".spectrum-Popover").contains("REGION_ID").click() .click({ force: true })
cy.get(".spectrum-Picker").eq(3).click() cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Popover").contains("COUNTRIES").click() cy.get(".spectrum-Picker").eq(0).click()
cy.get(".spectrum-Picker").eq(4).click() cy.get(".spectrum-Popover").contains("One").click()
cy.get(".spectrum-Popover").contains("REGION_ID").click() cy.get(".spectrum-Picker").eq(1).click()
// Save relationship & reload page cy.get(".spectrum-Popover").contains("REGIONS").click()
cy.get(".spectrum-Button").contains("Save").click({ force: true }) cy.get(".spectrum-Picker").eq(2).click()
cy.reload() cy.get(".spectrum-Popover").contains("REGION_ID").click()
}) cy.get(".spectrum-Picker").eq(3).click()
// Confirm table length & column name cy.get(".spectrum-Popover").contains("COUNTRIES").click()
cy.get(".spectrum-Table-body").eq(1) cy.get(".spectrum-Picker").eq(4).click()
.find('tr') cy.get(".spectrum-Popover").contains("REGION_ID").click()
.its('length') // Save relationship & reload page
.should('eq', 1) cy.get(".spectrum-Button").contains("Save").click({ force: true })
cy.get(".spectrum-Table-cell").should('contain', "COUNTRIES to REGIONS") cy.reload()
}) })
// Confirm table length & column name
it("should define a Many relationship type", () => { cy.get(".spectrum-Table")
// Select relationship type & configure .eq(1)
cy.get(".spectrum-Button").contains("Define relationship").click({ force: true }) .find(".spectrum-Table-row")
cy.get(".spectrum-Dialog-grid").within(() => { .its("length")
cy.get(".spectrum-Picker").eq(0).click() .should("eq", 1)
cy.get(".spectrum-Popover").contains("Many").click() cy.get(".spectrum-Table-cell").should("contain", "COUNTRIES to REGIONS")
cy.get(".spectrum-Picker").eq(1).click() })
cy.get(".spectrum-Popover").contains("LOCATIONS").click()
cy.get(".spectrum-Picker").eq(2).click() it("should define a Many relationship type", () => {
cy.get(".spectrum-Popover").contains("REGIONS").click() // Select relationship type & configure
cy.get(".spectrum-Picker").eq(3).click() cy.get(".spectrum-Button")
cy.get(".spectrum-Popover").contains("COUNTRIES").click() .contains("Define relationship")
cy.get(".spectrum-Picker").eq(4).click() .click({ force: true })
cy.get(".spectrum-Popover").contains("COUNTRY_ID").click() cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Picker").eq(5).click() cy.get(".spectrum-Picker").eq(0).click()
cy.get(".spectrum-Popover").contains("REGION_ID").click() cy.get(".spectrum-Popover").contains("Many").click()
// Save relationship & reload page cy.get(".spectrum-Picker").eq(1).click()
cy.get(".spectrum-Button").contains("Save").click({ force: true }) cy.get(".spectrum-Popover").contains("LOCATIONS").click()
cy.reload() cy.get(".spectrum-Picker").eq(2).click()
cy.wait(1000) cy.get(".spectrum-Popover").contains("REGIONS").click()
}) cy.get(".spectrum-Picker").eq(3).click()
// Confirm table length & relationship name cy.get(".spectrum-Popover").contains("COUNTRIES").click()
cy.get(".spectrum-Table-body").eq(1) cy.get(".spectrum-Picker").eq(4).click()
.find('tr') cy.get(".spectrum-Popover").contains("COUNTRY_ID").click()
.its('length') cy.get(".spectrum-Picker").eq(5).click()
.should('eq', 2) cy.get(".spectrum-Popover").contains("REGION_ID").click()
cy.get(".spectrum-Table-cell") // Save relationship & reload page
.should('contain', "LOCATIONS through COUNTRIES → REGIONS") cy.get(".spectrum-Button").contains("Save").click({ force: true })
}) cy.reload()
cy.wait(1000)
it("should delete relationships", () => { })
// Delete both relationships // Confirm table length & relationship name
cy.get(".spectrum-Table-body") cy.get(".spectrum-Table")
.eq(1).find('tr').its('length') .eq(1)
.then((len) => { .find(".spectrum-Table-row")
for (let i = 0; i < len; i++) { .its("length")
cy.get(".spectrum-Table-body").eq(1).within(() => { .should("eq", 2)
cy.get(".spectrum-Table-row").eq(0).click() cy.get(".spectrum-Table-cell").should(
cy.wait(500) "contain",
}) "LOCATIONS through COUNTRIES → REGIONS"
cy.get(".spectrum-Dialog-grid").within(() => { )
cy.get(".spectrum-Button").contains("Delete").click({ force: true }) })
})
cy.reload() it("should delete relationships", () => {
} // Delete both relationships
// Confirm relationships no longer exist cy.get(".spectrum-Table")
cy.get(".spectrum-Body").should('contain', 'No relationships configured') .eq(1)
}) .find(".spectrum-Table-row")
}) .its("length")
.then(len => {
it("should add a query", () => { for (let i = 0; i < len; i++) {
// Add query cy.get(".spectrum-Table")
cy.get(".spectrum-Button").contains("Add query").click({ force: true }) .eq(1)
cy.get(".spectrum-Form-item").eq(0).within(() => { .within(() => {
cy.get("input").type(queryName) cy.get(".spectrum-Table-row").eq(0).click()
}) cy.wait(500)
// Insert Query within Fields section
cy.get(".CodeMirror textarea").eq(0)
.type("SELECT * FROM books", { force: true })
// Intercept query execution
cy.intercept('**/queries/preview').as('query')
cy.get(".spectrum-Button").contains("Run Query").click({ force: true })
cy.wait(500)
cy.wait("@query")
// Assert against Status Code & Body
cy.get("@query").its('response.statusCode')
.should('eq', 200)
cy.get("@query").its('response.body')
.should('not.be.empty')
// Save query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
cy.get(".nav-item").should('contain', queryName)
})
it("should duplicate a query", () => {
// Get last nav item - The query
cy.get(".nav-item").last().within(() => {
cy.get(".icon").eq(1).click({ force: true })
})
// Select and confirm duplication
cy.get(".spectrum-Menu").contains("Duplicate").click()
cy.get(".nav-item").should('contain', queryName + ' (1)')
})
it("should edit a query name", () => {
// Rename query
cy.get(".spectrum-Form-item").eq(0).within(() => {
cy.get("input").clear().type(queryRename)
}) })
// Save query cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Button").contains("Save Query").click({ force: true }) cy.get(".spectrum-Button")
cy.get(".nav-item").should('contain', queryRename) .contains("Delete")
}) .click({ force: true })
})
it("should delete a query", () => { cy.reload()
// Get last nav item - The query }
for (let i = 0; i < 2; i++) { // Confirm relationships no longer exist
cy.get(".nav-item").last().within(() => { cy.get(".spectrum-Body").should(
cy.get(".icon").eq(1).click({ force: true }) "contain",
}) "No relationships configured"
// Select Delete )
cy.get(".spectrum-Menu").contains("Delete").click() })
cy.get(".spectrum-Button").contains("Delete Query").click({ force: true }) })
cy.wait(1000)
} it("should add a query", () => {
// Confirm deletion // Add query
cy.get(".nav-item").should('not.contain', queryName) cy.get(".spectrum-Button").contains("Add query").click({ force: true })
cy.get(".nav-item").should('not.contain', queryRename) cy.get(".spectrum-Form-item")
.eq(0)
.within(() => {
cy.get("input").type(queryName)
})
// Insert Query within Fields section
cy.get(".CodeMirror textarea")
.eq(0)
.type("SELECT * FROM books", { force: true })
// Intercept query execution
cy.intercept("**/queries/preview").as("query")
cy.get(".spectrum-Button").contains("Run Query").click({ force: true })
cy.wait(500)
cy.wait("@query")
// Assert against Status Code & Body
cy.get("@query").its("response.statusCode").should("eq", 200)
cy.get("@query").its("response.body").should("not.be.empty")
// Save query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
cy.get(".nav-item").should("contain", queryName)
})
it("should duplicate a query", () => {
// Get last nav item - The query
cy.get(".nav-item")
.last()
.within(() => {
cy.get(".icon").eq(1).click({ force: true })
})
// Select and confirm duplication
cy.get(".spectrum-Menu").contains("Duplicate").click()
cy.get(".nav-item").should("contain", queryName + " (1)")
})
it("should edit a query name", () => {
// Rename query
cy.get(".spectrum-Form-item")
.eq(0)
.within(() => {
cy.get("input").clear().type(queryRename)
})
// Save query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
cy.get(".nav-item").should("contain", queryRename)
})
it("should delete a query", () => {
// Get last nav item - The query
for (let i = 0; i < 2; i++) {
cy.get(".nav-item")
.last()
.within(() => {
cy.get(".icon").eq(1).click({ force: true })
}) })
// Select Delete
cy.get(".spectrum-Menu").contains("Delete").click()
cy.get(".spectrum-Button")
.contains("Delete Query")
.click({ force: true })
cy.wait(1000)
} }
}) // Confirm deletion
cy.get(".nav-item").should("not.contain", queryName)
cy.get(".nav-item").should("not.contain", queryRename)
})
}
})
}) })

416
packages/builder/cypress/integration/datasources/oracle.spec.js

@ -1,196 +1,230 @@
import filterTests from "../../support/filterTests" import filterTests from "../../support/filterTests"
filterTests(['all'], () => { filterTests(["all"], () => {
context("Oracle Datasource Testing", () => { context("Oracle Datasource Testing", () => {
if (Cypress.env("TEST_ENV")) { if (Cypress.env("TEST_ENV")) {
before(() => {
cy.login()
cy.createTestApp()
})
const datasource = "Oracle"
const queryName = "Cypress Test Query"
const queryRename = "CT Query Rename"
before(() => { it("Should add Oracle data source and skip table fetch", () => {
cy.login() // Select Oracle data source
cy.createTestApp() cy.selectExternalDatasource(datasource)
}) // Skip table fetch - no config added
const datasource = "Oracle" cy.get(".spectrum-Button")
const queryName = "Cypress Test Query" .contains("Skip table fetch")
const queryRename = "CT Query Rename" .click({ force: true })
cy.wait(500)
it("Should add Oracle data source and skip table fetch", () => { // Confirm config contains localhost
// Select Oracle data source cy.get(".spectrum-Textfield-input")
cy.selectExternalDatasource(datasource) .eq(1)
// Skip table fetch - no config added .should("have.value", "localhost")
cy.get(".spectrum-Button").contains("Skip table fetch").click({ force: true }) // Add another Oracle data source, configure & skip table fetch
cy.wait(500) cy.selectExternalDatasource(datasource)
// Confirm config contains localhost cy.addDatasourceConfig(datasource, true)
cy.get(".spectrum-Textfield-input").eq(1).should('have.value', 'localhost') // Confirm config and no tables
// Add another Oracle data source, configure & skip table fetch cy.get(".spectrum-Textfield-input")
cy.selectExternalDatasource(datasource) .eq(1)
cy.addDatasourceConfig(datasource, true) .should("have.value", Cypress.env("oracle").HOST)
// Confirm config and no tables cy.get(".spectrum-Body").eq(2).should("contain", "No tables found.")
cy.get(".spectrum-Textfield-input").eq(1).should('have.value', Cypress.env("oracle").HOST) })
cy.get(".spectrum-Body").eq(2).should('contain', 'No tables found.')
}) it("Should add Oracle data source and fetch tables without configuration", () => {
// Select Oracle data source
it("Should add Oracle data source and fetch tables without configuration", () => { cy.selectExternalDatasource(datasource)
// Select Oracle data source // Attempt to fetch tables without applying configuration
cy.selectExternalDatasource(datasource) cy.intercept("**/datasources").as("datasource")
// Attempt to fetch tables without applying configuration cy.get(".spectrum-Button")
cy.intercept('**/datasources').as('datasource') .contains("Save and fetch tables")
cy.get(".spectrum-Button") .click({ force: true })
.contains("Save and fetch tables") // Intercept Request after button click & apply assertions
.click({ force: true }) cy.wait("@datasource")
// Intercept Request after button click & apply assertions cy.get("@datasource")
cy.wait("@datasource") .its("response.body")
cy.get("@datasource").its('response.body') .should("have.property", "status", 500)
.should('have.property', 'status', 500) })
})
it("should add Oracle data source and fetch tables", () => {
it("should add Oracle data source and fetch tables", () => { // Add & configure Oracle data source
// Add & configure Oracle data source cy.selectExternalDatasource(datasource)
cy.selectExternalDatasource(datasource) cy.intercept("**/datasources").as("datasource")
cy.intercept('**/datasources').as('datasource') cy.addDatasourceConfig(datasource)
cy.addDatasourceConfig(datasource) // Check response from datasource after adding configuration
// Check response from datasource after adding configuration cy.wait("@datasource")
cy.wait("@datasource") cy.get("@datasource").its("response.statusCode").should("eq", 200)
cy.get("@datasource").its('response.statusCode') // Confirm fetch tables was successful
.should('eq', 200) cy.get(".spectrum-Table")
// Confirm fetch tables was successful .eq(0)
cy.get(".spectrum-Table-body").eq(0) .find(".spectrum-Table-row")
.find('tr') .its("length")
.its('length') .should("be.gt", 0)
.should('be.gt', 0) })
})
it("should define a One relationship type", () => {
it("should define a One relationship type", () => { // Select relationship type & configure
// Select relationship type & configure cy.get(".spectrum-Button")
cy.get(".spectrum-Button").contains("Define relationship").click({ force: true }) .contains("Define relationship")
cy.get(".spectrum-Dialog-grid").within(() => { .click({ force: true })
cy.get(".spectrum-Picker").eq(0).click() cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Popover").contains("One").click() cy.get(".spectrum-Picker").eq(0).click()
cy.get(".spectrum-Picker").eq(1).click() cy.get(".spectrum-Popover").contains("One").click()
cy.get(".spectrum-Popover").contains("REGIONS").click() cy.get(".spectrum-Picker").eq(1).click()
cy.get(".spectrum-Picker").eq(2).click() cy.get(".spectrum-Popover").contains("REGIONS").click()
cy.get(".spectrum-Popover").contains("REGION_ID").click() cy.get(".spectrum-Picker").eq(2).click()
cy.get(".spectrum-Picker").eq(3).click() cy.get(".spectrum-Popover").contains("REGION_ID").click()
cy.get(".spectrum-Popover").contains("COUNTRIES").click() cy.get(".spectrum-Picker").eq(3).click()
cy.get(".spectrum-Picker").eq(4).click() cy.get(".spectrum-Popover").contains("COUNTRIES").click()
cy.get(".spectrum-Popover").contains("REGION_ID").click() cy.get(".spectrum-Picker").eq(4).click()
// Save relationship & reload page cy.get(".spectrum-Popover").contains("REGION_ID").click()
cy.get(".spectrum-Button").contains("Save").click({ force: true }) // Save relationship & reload page
cy.reload() cy.get(".spectrum-Button").contains("Save").click({ force: true })
}) cy.reload()
// Confirm table length & column name })
cy.get(".spectrum-Table-body").eq(1) // Confirm table length & column name
.find('tr') cy.get(".spectrum-Table")
.its('length') .eq(1)
.should('eq', 1) .find(".spectrum-Table-row")
cy.get(".spectrum-Table-cell").should('contain', "COUNTRIES to REGIONS") .its("length")
}) .should("eq", 1)
cy.get(".spectrum-Table-cell").should("contain", "COUNTRIES to REGIONS")
it("should define a Many relationship type", () => { })
// Select relationship type & configure
cy.get(".spectrum-Button").contains("Define relationship").click({ force: true }) it("should define a Many relationship type", () => {
cy.get(".spectrum-Dialog-grid").within(() => { // Select relationship type & configure
cy.get(".spectrum-Picker").eq(0).click() cy.get(".spectrum-Button")
cy.get(".spectrum-Popover").contains("Many").click() .contains("Define relationship")
cy.get(".spectrum-Picker").eq(1).click() .click({ force: true })
cy.get(".spectrum-Popover").contains("LOCATIONS").click() cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Picker").eq(2).click() cy.get(".spectrum-Picker").eq(0).click()
cy.get(".spectrum-Popover").contains("REGIONS").click() cy.get(".spectrum-Popover").contains("Many").click()
cy.get(".spectrum-Picker").eq(3).click() cy.get(".spectrum-Picker").eq(1).click()
cy.get(".spectrum-Popover").contains("COUNTRIES").click() cy.get(".spectrum-Popover").contains("LOCATIONS").click()
cy.get(".spectrum-Picker").eq(4).click() cy.get(".spectrum-Picker").eq(2).click()
cy.get(".spectrum-Popover").contains("COUNTRY_ID").click() cy.get(".spectrum-Popover").contains("REGIONS").click()
cy.get(".spectrum-Picker").eq(5).click() cy.get(".spectrum-Picker").eq(3).click()
cy.get(".spectrum-Popover").contains("REGION_ID").click() cy.get(".spectrum-Popover").contains("COUNTRIES").click()
// Save relationship & reload page cy.get(".spectrum-Picker").eq(4).click()
cy.get(".spectrum-Button").contains("Save").click({ force: true }) cy.get(".spectrum-Popover").contains("COUNTRY_ID").click()
cy.reload() cy.get(".spectrum-Picker").eq(5).click()
}) cy.get(".spectrum-Popover").contains("REGION_ID").click()
// Confirm table length & relationship name // Save relationship & reload page
cy.get(".spectrum-Table-body").eq(1) cy.get(".spectrum-Button").contains("Save").click({ force: true })
.find('tr') cy.reload()
.its('length') })
.should('eq', 2) // Confirm table length & relationship name
cy.get(".spectrum-Table-cell") cy.get(".spectrum-Table")
.should('contain', "LOCATIONS through COUNTRIES → REGIONS") .eq(1)
}) .find(".spectrum-Table-row")
.its("length")
it("should delete relationships", () => { .should("eq", 2)
// Delete both relationships cy.get(".spectrum-Table-cell").should(
cy.get(".spectrum-Table-body") "contain",
.eq(1).find('tr').its('length') "LOCATIONS through COUNTRIES → REGIONS"
.then((len) => { )
for (let i = 0; i < len; i++) { })
cy.get(".spectrum-Table-body").eq(1).within(() => {
cy.get(".spectrum-Table-row").eq(0).click() it("should delete relationships", () => {
cy.wait(500) // Delete both relationships
}) cy.get(".spectrum-Table")
cy.get(".spectrum-Dialog-grid").within(() => { .eq(1)
cy.get(".spectrum-Button").contains("Delete").click({ force: true }) .find(".spectrum-Table-row")
}) .its("length")
cy.reload() .then(len => {
} for (let i = 0; i < len; i++) {
// Confirm relationships no longer exist cy.get(".spectrum-Table")
cy.get(".spectrum-Body").should('contain', 'No relationships configured') .eq(1)
}) .within(() => {
}) cy.get(".spectrum-Table-row").eq(0).click()
cy.wait(500)
it("should add a query", () => {
// Add query
cy.get(".spectrum-Button").contains("Add query").click({ force: true })
cy.get(".spectrum-Form-item").eq(0).within(() => {
cy.get("input").type(queryName)
})
// Insert Query within Fields section
cy.get(".CodeMirror textarea").eq(0)
.type("SELECT * FROM JOBS", { force: true })
// Intercept query execution
cy.intercept('**/queries/preview').as('query')
cy.get(".spectrum-Button").contains("Run Query").click({ force: true })
cy.wait(500)
cy.wait("@query")
// Assert against Status Code & Body
cy.get("@query").its('response.statusCode')
.should('eq', 200)
cy.get("@query").its('response.body')
.should('not.be.empty')
// Save query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
cy.get(".nav-item").should('contain', queryName)
})
it("should duplicate a query", () => {
// Get query nav item
cy.get(".nav-item").contains(queryName).parent().within(() => {
cy.get(".spectrum-Icon").eq(1).click({ force: true })
})
// Select and confirm duplication
cy.get(".spectrum-Menu").contains("Duplicate").click()
cy.get(".nav-item").should('contain', queryName + ' (1)')
})
it("should edit a query name", () => {
// Rename query
cy.get(".spectrum-Form-item").eq(0).within(() => {
cy.get("input").clear().type(queryRename)
})
// Save query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
cy.get(".nav-item").should('contain', queryRename)
})
it("should delete a query", () => {
// Get query nav item - QueryName
cy.get(".nav-item").contains(queryName).parent().within(() => {
cy.get(".spectrum-Icon").eq(1).click({ force: true })
}) })
cy.get(".spectrum-Dialog-grid").within(() => {
// Select Delete cy.get(".spectrum-Button")
cy.get(".spectrum-Menu").contains("Delete").click() .contains("Delete")
cy.get(".spectrum-Button").contains("Delete Query").click({ force: true }) .click({ force: true })
cy.wait(1000) })
cy.reload()
// Confirm deletion }
cy.get(".nav-item").should('not.contain', queryName) // Confirm relationships no longer exist
}) cy.get(".spectrum-Body").should(
} "contain",
}) "No relationships configured"
)
})
})
it("should add a query", () => {
// Add query
cy.get(".spectrum-Button").contains("Add query").click({ force: true })
cy.get(".spectrum-Form-item")
.eq(0)
.within(() => {
cy.get("input").type(queryName)
})
// Insert Query within Fields section
cy.get(".CodeMirror textarea")
.eq(0)
.type("SELECT * FROM JOBS", { force: true })
// Intercept query execution
cy.intercept("**/queries/preview").as("query")
cy.get(".spectrum-Button").contains("Run Query").click({ force: true })
cy.wait(500)
cy.wait("@query")
// Assert against Status Code & Body
cy.get("@query").its("response.statusCode").should("eq", 200)
cy.get("@query").its("response.body").should("not.be.empty")
// Save query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
cy.get(".nav-item").should("contain", queryName)
})
it("should duplicate a query", () => {
// Get query nav item
cy.get(".nav-item")
.contains(queryName)
.parent()
.within(() => {
cy.get(".spectrum-Icon").eq(1).click({ force: true })
})
// Select and confirm duplication
cy.get(".spectrum-Menu").contains("Duplicate").click()
cy.get(".nav-item").should("contain", queryName + " (1)")
})
it("should edit a query name", () => {
// Rename query
cy.get(".spectrum-Form-item")
.eq(0)
.within(() => {
cy.get("input").clear().type(queryRename)
})
// Save query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
cy.get(".nav-item").should("contain", queryRename)
})
it("should delete a query", () => {
// Get query nav item - QueryName
cy.get(".nav-item")
.contains(queryName)
.parent()
.within(() => {
cy.get(".spectrum-Icon").eq(1).click({ force: true })
})
// Select Delete
cy.get(".spectrum-Menu").contains("Delete").click()
cy.get(".spectrum-Button")
.contains("Delete Query")
.click({ force: true })
cy.wait(1000)
// Confirm deletion
cy.get(".nav-item").should("not.contain", queryName)
})
}
})
}) })

516
packages/builder/cypress/integration/datasources/postgreSql.spec.js

@ -1,241 +1,285 @@
import filterTests from "../../support/filterTests" import filterTests from "../../support/filterTests"
filterTests(['all'], () => { filterTests(["all"], () => {
context("PostgreSQL Datasource Testing", () => { context("PostgreSQL Datasource Testing", () => {
if (Cypress.env("TEST_ENV")) { if (Cypress.env("TEST_ENV")) {
before(() => {
before(() => { cy.login()
cy.login() cy.createTestApp()
cy.createTestApp() })
}) const datasource = "PostgreSQL"
const datasource = "PostgreSQL" const queryName = "Cypress Test Query"
const queryName = "Cypress Test Query" const queryRename = "CT Query Rename"
const queryRename = "CT Query Rename"
it("Should add PostgreSQL data source without configuration", () => {
it("Should add PostgreSQL data source without configuration", () => { // Select PostgreSQL data source
// Select PostgreSQL data source cy.selectExternalDatasource(datasource)
cy.selectExternalDatasource(datasource) // Attempt to fetch tables without applying configuration
// Attempt to fetch tables without applying configuration cy.intercept("**/datasources").as("datasource")
cy.intercept('**/datasources').as('datasource') cy.get(".spectrum-Button")
cy.get(".spectrum-Button") .contains("Save and fetch tables")
.contains("Save and fetch tables") .click({ force: true })
.click({ force: true }) // Intercept Request after button click & apply assertions
// Intercept Request after button click & apply assertions cy.wait("@datasource")
cy.wait("@datasource") cy.get("@datasource")
cy.get("@datasource").its('response.body') .its("response.body")
.should('have.property', 'message', 'connect ECONNREFUSED 127.0.0.1:5432') .should(
cy.get("@datasource").its('response.body') "have.property",
.should('have.property', 'status', 500) "message",
}) "connect ECONNREFUSED 127.0.0.1:5432"
)
it("should add PostgreSQL data source and fetch tables", () => { cy.get("@datasource")
// Add & configure PostgreSQL data source .its("response.body")
cy.selectExternalDatasource(datasource) .should("have.property", "status", 500)
cy.intercept('**/datasources').as('datasource') })
cy.addDatasourceConfig(datasource)
// Check response from datasource after adding configuration it("should add PostgreSQL data source and fetch tables", () => {
cy.wait("@datasource") // Add & configure PostgreSQL data source
cy.get("@datasource").its('response.statusCode') cy.selectExternalDatasource(datasource)
.should('eq', 200) cy.intercept("**/datasources").as("datasource")
// Confirm fetch tables was successful cy.addDatasourceConfig(datasource)
cy.get(".spectrum-Table-body").eq(0) // Check response from datasource after adding configuration
.find('tr') cy.wait("@datasource")
.its('length') cy.get("@datasource").its("response.statusCode").should("eq", 200)
.should('be.gt', 0) // Confirm fetch tables was successful
}) cy.get(".spectrum-Table")
.eq(0)
it("should define a One relationship type", () => { .find(".spectrum-Table-row")
// Select relationship type & configure .its("length")
cy.get(".spectrum-Button").contains("Define relationship").click({ force: true }) .should("be.gt", 0)
cy.get(".spectrum-Dialog-grid").within(() => { })
cy.get(".spectrum-Picker").eq(0).click()
cy.get(".spectrum-Popover").contains("One").click() it("should define a One relationship type", () => {
cy.get(".spectrum-Picker").eq(1).click() // Select relationship type & configure
cy.get(".spectrum-Popover").contains("REGIONS").click() cy.get(".spectrum-Button")
cy.get(".spectrum-Picker").eq(2).click() .contains("Define relationship")
cy.get(".spectrum-Popover").contains("REGION_ID").click() .click({ force: true })
cy.get(".spectrum-Picker").eq(3).click() cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Popover").contains("COUNTRIES").click() cy.get(".spectrum-Picker").eq(0).click()
cy.get(".spectrum-Picker").eq(4).click() cy.get(".spectrum-Popover").contains("One").click()
cy.get(".spectrum-Popover").contains("REGION_ID").click() cy.get(".spectrum-Picker").eq(1).click()
// Save relationship & reload page cy.get(".spectrum-Popover").contains("REGIONS").click()
cy.get(".spectrum-Button").contains("Save").click({ force: true }) cy.get(".spectrum-Picker").eq(2).click()
cy.reload() cy.get(".spectrum-Popover").contains("REGION_ID").click()
}) cy.get(".spectrum-Picker").eq(3).click()
// Confirm table length & column name cy.get(".spectrum-Popover").contains("COUNTRIES").click()
cy.get(".spectrum-Table-body").eq(1) cy.get(".spectrum-Picker").eq(4).click()
.find('tr') cy.get(".spectrum-Popover").contains("REGION_ID").click()
.its('length') // Save relationship & reload page
.should('eq', 1) cy.get(".spectrum-Button").contains("Save").click({ force: true })
cy.get(".spectrum-Table-cell").should('contain', "COUNTRIES to REGIONS") cy.reload()
}) })
// Confirm table length & column name
it("should define a Many relationship type", () => { cy.get(".spectrum-Table")
// Select relationship type & configure .eq(1)
cy.get(".spectrum-Button").contains("Define relationship").click({ force: true }) .find(".spectrum-Table-row")
cy.get(".spectrum-Dialog-grid").within(() => { .its("length")
cy.get(".spectrum-Picker").eq(0).click() .should("eq", 1)
cy.get(".spectrum-Popover").contains("Many").click() cy.get(".spectrum-Table-cell").should("contain", "COUNTRIES to REGIONS")
cy.get(".spectrum-Picker").eq(1).click() })
cy.get(".spectrum-Popover").contains("LOCATIONS").click()
cy.get(".spectrum-Picker").eq(2).click() it("should define a Many relationship type", () => {
cy.get(".spectrum-Popover").contains("REGIONS").click() // Select relationship type & configure
cy.get(".spectrum-Picker").eq(3).click() cy.get(".spectrum-Button")
cy.get(".spectrum-Popover").contains("COUNTRIES").click() .contains("Define relationship")
cy.get(".spectrum-Picker").eq(4).click() .click({ force: true })
cy.get(".spectrum-Popover").contains("COUNTRY_ID").click() cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Picker").eq(5).click() cy.get(".spectrum-Picker").eq(0).click()
cy.get(".spectrum-Popover").contains("REGION_ID").click() cy.get(".spectrum-Popover").contains("Many").click()
// Save relationship & reload page cy.get(".spectrum-Picker").eq(1).click()
cy.get(".spectrum-Button").contains("Save").click({ force: true }) cy.get(".spectrum-Popover").contains("LOCATIONS").click()
cy.reload() cy.get(".spectrum-Picker").eq(2).click()
}) cy.get(".spectrum-Popover").contains("REGIONS").click()
// Confirm table length & relationship name cy.get(".spectrum-Picker").eq(3).click()
cy.get(".spectrum-Table-body").eq(1) cy.get(".spectrum-Popover").contains("COUNTRIES").click()
.find('tr') cy.get(".spectrum-Picker").eq(4).click()
.its('length') cy.get(".spectrum-Popover").contains("COUNTRY_ID").click()
.should('eq', 2) cy.get(".spectrum-Picker").eq(5).click()
cy.get(".spectrum-Table-cell") cy.get(".spectrum-Popover").contains("REGION_ID").click()
.should('contain', "LOCATIONS through COUNTRIES → REGIONS") // Save relationship & reload page
}) cy.get(".spectrum-Button").contains("Save").click({ force: true })
cy.reload()
it("should delete a relationship", () => { })
cy.get(".hierarchy-items-container").contains(datasource).click() // Confirm table length & relationship name
cy.reload() cy.get(".spectrum-Table")
// Delete one relationship .eq(1)
cy.get(".spectrum-Table-body").eq(1).within(() => { .find(".spectrum-Table-row")
cy.get(".spectrum-Table-row").eq(0).click() .its("length")
cy.wait(500) .should("eq", 2)
}) cy.get(".spectrum-Table-cell").should(
cy.get(".spectrum-Dialog-grid").within(() => { "contain",
cy.get(".spectrum-Button").contains("Delete").click({ force: true }) "LOCATIONS through COUNTRIES → REGIONS"
}) )
cy.reload() })
// Confirm relationship was deleted
cy.get(".spectrum-Table-body") it("should delete a relationship", () => {
.eq(1).find('tr').its('length').should('eq', 1) cy.get(".hierarchy-items-container").contains(datasource).click()
}) cy.reload()
// Delete one relationship
it("should add a query", () => { cy.get(".spectrum-Table")
// Add query .eq(1)
cy.get(".spectrum-Button").contains("Add query").click({ force: true }) .within(() => {
cy.get(".spectrum-Form-item").eq(0).within(() => { cy.get(".spectrum-Table-row").eq(0).click()
cy.get("input").type(queryName) cy.wait(500)
}) })
// Insert Query within Fields section cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".CodeMirror textarea").eq(0) cy.get(".spectrum-Button").contains("Delete").click({ force: true })
.type("SELECT * FROM books", { force: true }) })
// Intercept query execution cy.reload()
cy.intercept('**/queries/preview').as('query') // Confirm relationship was deleted
cy.get(".spectrum-Button").contains("Run Query").click({ force: true }) cy.get(".spectrum-Table")
cy.wait(500) .eq(1)
cy.wait("@query") .find(".spectrum-Table-row")
// Assert against Status Code & Body .its("length")
cy.get("@query").its('response.statusCode') .should("eq", 1)
.should('eq', 200) })
cy.get("@query").its('response.body')
.should('not.be.empty') it("should add a query", () => {
// Save query // Add query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true }) cy.get(".spectrum-Button").contains("Add query").click({ force: true })
cy.get(".hierarchy-items-container").should('contain', queryName) cy.get(".spectrum-Form-item")
}) .eq(0)
.within(() => {
it("should switch to schema with no tables", () => { cy.get("input").type(queryName)
// Switch Schema - To one without any tables })
cy.get(".hierarchy-items-container").contains(datasource).click() // Insert Query within Fields section
switchSchema("randomText") cy.get(".CodeMirror textarea")
.eq(0)
// No tables displayed .type("SELECT * FROM books", { force: true })
cy.get(".spectrum-Body").eq(2).should('contain', 'No tables found') // Intercept query execution
cy.intercept("**/queries/preview").as("query")
// Previously created query should be visible cy.get(".spectrum-Button").contains("Run Query").click({ force: true })
cy.get(".spectrum-Table-body").should('contain', queryName) cy.wait(500)
}) cy.wait("@query")
// Assert against Status Code & Body
it("should switch schemas", () => { cy.get("@query").its("response.statusCode").should("eq", 200)
// Switch schema - To one with tables cy.get("@query").its("response.body").should("not.be.empty")
switchSchema("1") // Save query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
// Confirm tables exist - Check for specific one cy.get(".hierarchy-items-container").should("contain", queryName)
cy.get(".spectrum-Table-body").eq(0).should('contain', 'test') })
cy.get(".spectrum-Table-body").eq(0).find('tr').its('length').should('eq', 1)
it("should switch to schema with no tables", () => {
// Confirm specific table visible within left nav bar // Switch Schema - To one without any tables
cy.get(".hierarchy-items-container").should('contain', 'test') cy.get(".hierarchy-items-container").contains(datasource).click()
switchSchema("randomText")
// Switch back to public schema
switchSchema("public") // No tables displayed
cy.get(".spectrum-Body").eq(2).should("contain", "No tables found")
// Confirm tables exist - again
cy.get(".spectrum-Table-body").eq(0).should('contain', 'REGIONS') // Previously created query should be visible
cy.get(".spectrum-Table-body").eq(0) cy.get(".spectrum-Table").should("contain", queryName)
.find('tr').its('length').should('be.gt', 1) })
// Confirm specific table visible within left nav bar it("should switch schemas", () => {
cy.get(".hierarchy-items-container").should('contain', 'REGIONS') // Switch schema - To one with tables
switchSchema("1")
// No relationships and one query
cy.get(".spectrum-Body").eq(3).should('contain', 'No relationships configured.') // Confirm tables exist - Check for specific one
cy.get(".spectrum-Table-body").eq(1).should('contain', queryName) cy.get(".spectrum-Table").eq(0).should("contain", "test")
}) cy.get(".spectrum-Table")
.eq(0)
it("should duplicate a query", () => { .find(".spectrum-Table-row")
// Get last nav item - The query .its("length")
cy.get(".nav-item").last().within(() => { .should("eq", 1)
cy.get(".icon").eq(1).click({ force: true })
}) // Confirm specific table visible within left nav bar
// Select and confirm duplication cy.get(".hierarchy-items-container").should("contain", "test")
cy.get(".spectrum-Menu").contains("Duplicate").click()
cy.get(".nav-item").should('contain', queryName + ' (1)') // Switch back to public schema
}) switchSchema("public")
it("should edit a query name", () => { // Confirm tables exist - again
// Access query cy.get(".spectrum-Table").eq(0).should("contain", "REGIONS")
cy.get(".hierarchy-items-container").contains(queryName + ' (1)').click() cy.get(".spectrum-Table")
.eq(0)
// Rename query .find(".spectrum-Table-row")
cy.get(".spectrum-Form-item").eq(0).within(() => { .its("length")
cy.get("input").clear().type(queryRename) .should("be.gt", 1)
})
// Confirm specific table visible within left nav bar
// Run and Save query cy.get(".hierarchy-items-container").should("contain", "REGIONS")
cy.get(".spectrum-Button").contains("Run Query").click({ force: true })
cy.wait(500) // No relationships and one query
cy.get(".spectrum-Button").contains("Save Query").click({ force: true }) cy.get(".spectrum-Body")
cy.get(".nav-item").should('contain', queryRename) .eq(3)
}) .should("contain", "No relationships configured.")
cy.get(".spectrum-Table").eq(1).should("contain", queryName)
it("should delete a query", () => { })
// Get last nav item - The query
for (let i = 0; i < 2; i++) { it("should duplicate a query", () => {
cy.get(".nav-item").last().within(() => { // Get last nav item - The query
cy.get(".icon").eq(1).click({ force: true }) cy.get(".nav-item")
}) .last()
// Select Delete .within(() => {
cy.get(".spectrum-Menu").contains("Delete").click() cy.get(".icon").eq(1).click({ force: true })
cy.get(".spectrum-Button").contains("Delete Query").click({ force: true }) })
cy.wait(1000) // Select and confirm duplication
} cy.get(".spectrum-Menu").contains("Duplicate").click()
// Confirm deletion cy.get(".nav-item").should("contain", queryName + " (1)")
cy.get(".nav-item").should('not.contain', queryName) })
cy.get(".nav-item").should('not.contain', queryRename)
it("should edit a query name", () => {
// Access query
cy.get(".hierarchy-items-container")
.contains(queryName + " (1)")
.click()
// Rename query
cy.get(".spectrum-Form-item")
.eq(0)
.within(() => {
cy.get("input").clear().type(queryRename)
})
// Run and Save query
cy.get(".spectrum-Button").contains("Run Query").click({ force: true })
cy.wait(500)
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
cy.get(".nav-item").should("contain", queryRename)
})
it("should delete a query", () => {
// Get last nav item - The query
for (let i = 0; i < 2; i++) {
cy.get(".nav-item")
.last()
.within(() => {
cy.get(".icon").eq(1).click({ force: true })
}) })
// Select Delete
const switchSchema = (schema) => { cy.get(".spectrum-Menu").contains("Delete").click()
// Edit configuration - Change Schema cy.get(".spectrum-Button")
cy.get(".spectrum-Textfield").eq(6).within(() => { .contains("Delete Query")
cy.get('input').clear().type(schema) .click({ force: true })
}) cy.wait(1000)
// Save configuration & fetch
cy.get(".spectrum-Button").contains("Save").click({ force: true })
cy.get(".spectrum-Button").contains("Fetch tables").click({ force: true })
// Click fetch tables again within modal
cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Button").contains("Fetch tables").click({ force: true })
})
cy.reload()
cy.wait(5000)
}
} }
}) // Confirm deletion
cy.get(".nav-item").should("not.contain", queryName)
cy.get(".nav-item").should("not.contain", queryRename)
})
const switchSchema = schema => {
// Edit configuration - Change Schema
cy.get(".spectrum-Textfield")
.eq(6)
.within(() => {
cy.get("input").clear().type(schema)
})
// Save configuration & fetch
cy.get(".spectrum-Button").contains("Save").click({ force: true })
cy.get(".spectrum-Button")
.contains("Fetch tables")
.click({ force: true })
// Click fetch tables again within modal
cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".spectrum-Button")
.contains("Fetch tables")
.click({ force: true })
})
cy.reload()
cy.wait(5000)
}
}
})
}) })

2
packages/builder/src/components/backend/DataTable/DataTable.svelte

@ -98,7 +98,7 @@
tableId={id} tableId={id}
data={$fetch.rows} data={$fetch.rows}
bind:hideAutocolumns bind:hideAutocolumns
loading={$fetch.loading} loading={!$fetch.loaded}
on:sort={onSort} on:sort={onSort}
allowEditing allowEditing
disableSorting disableSorting

2
packages/builder/src/components/backend/DataTable/Table.svelte

@ -136,7 +136,7 @@
</div> </div>
</div> </div>
{#key tableId} {#key tableId}
<div class="table-wrapper" in:fade={{ delay: 200, duration: 100 }}> <div class="table-wrapper">
<Table <Table
{data} {data}
{schema} {schema}

66
packages/builder/src/components/design/PropertiesPanel/PropertyControls/ColumnEditor/CellDrawer.svelte

@ -0,0 +1,66 @@
<script>
import {
Input,
Select,
ColorPicker,
DrawerContent,
Layout,
Label,
} from "@budibase/bbui"
import { store } from "builderStore"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
export let column
</script>
<DrawerContent>
<div class="container">
<Layout noPadding gap="S">
<Input bind:value={column.width} label="Width" placeholder="Auto" />
<Select
label="Alignment"
bind:value={column.align}
options={["Left", "Center", "Right"]}
placeholder="Default"
/>
<DrawerBindableInput
label="Value"
value={column.template}
on:change={e => (column.template = e.detail)}
placeholder={`{{ Value }}`}
bindings={[
{
readableBinding: "Value",
runtimeBinding: "[value]",
},
]}
/>
<Layout noPadding gap="XS">
<Label>Background color</Label>
<ColorPicker
value={column.background}
on:change={e => (column.background = e.detail)}
alignRight
spectrumTheme={$store.theme}
/>
</Layout>
<Layout noPadding gap="XS">
<Label>Text color</Label>
<ColorPicker
value={column.color}
on:change={e => (column.color = e.detail)}
alignRight
spectrumTheme={$store.theme}
/>
</Layout>
</Layout>
</div>
</DrawerContent>
<style>
.container {
width: 100%;
max-width: 240px;
margin: 0 auto;
}
</style>

34
packages/builder/src/components/design/PropertiesPanel/PropertyControls/ColumnEditor/CellEditor.svelte

@ -0,0 +1,34 @@
<script>
import { Drawer, Button, Icon } from "@budibase/bbui"
import CellDrawer from "./CellDrawer.svelte"
export let column
let boundValue
let drawer
$: updateBoundValue(column)
const updateBoundValue = value => {
boundValue = { ...value }
}
const open = () => {
updateBoundValue(column)
drawer.show()
}
const save = () => {
column = boundValue
drawer.hide()
}
</script>
<Icon name="Settings" hoverable size="S" on:click={open} />
<Drawer bind:this={drawer} title="Table Columns">
<svelte:fragment slot="description">
"{column.name}" column settings
</svelte:fragment>
<Button cta slot="buttons" on:click={save}>Save</Button>
<CellDrawer slot="body" bind:column={boundValue} />
</Drawer>

57
packages/builder/src/components/design/PropertiesPanel/PropertyControls/ColumnEditor/ColumnDrawer.svelte

@ -4,17 +4,19 @@
Icon, Icon,
DrawerContent, DrawerContent,
Layout, Layout,
Input,
Select, Select,
Label, Label,
Body, Body,
Input,
} from "@budibase/bbui" } from "@budibase/bbui"
import { flip } from "svelte/animate" import { flip } from "svelte/animate"
import { dndzone } from "svelte-dnd-action" import { dndzone } from "svelte-dnd-action"
import { generate } from "shortid" import { generate } from "shortid"
import CellEditor from "./CellEditor.svelte"
export let columns = [] export let columns = []
export let options = [] export let options = []
export let schema = {}
const flipDurationMs = 150 const flipDurationMs = 150
let dragDisabled = true let dragDisabled = true
@ -61,11 +63,23 @@
dragDisabled = true dragDisabled = true
} }
const addAllColumns = () => {
let newColumns = columns || []
options.forEach(field => {
const fieldSchema = schema[field]
const hasCol = columns && columns.findIndex(x => x.name === field) !== -1
if (!fieldSchema?.autocolumn && !hasCol) {
newColumns.push({
name: field,
displayName: field,
})
}
})
columns = newColumns
}
const reset = () => { const reset = () => {
columns = options.map(col => ({ columns = []
name: col,
displayName: col,
}))
} }
</script> </script>
@ -79,6 +93,7 @@
<Label size="L">Column</Label> <Label size="L">Column</Label>
<Label size="L">Label</Label> <Label size="L">Label</Label>
<div /> <div />
<div />
</div> </div>
<div <div
class="columns" class="columns"
@ -108,6 +123,7 @@
on:change={e => (column.displayName = e.detail)} on:change={e => (column.displayName = e.detail)}
/> />
<Input bind:value={column.displayName} placeholder="Label" /> <Input bind:value={column.displayName} placeholder="Label" />
<CellEditor bind:column />
<Icon <Icon
name="Close" name="Close"
hoverable hoverable
@ -121,19 +137,25 @@
</Layout> </Layout>
{:else} {:else}
<div class="column"> <div class="column">
<div /> <div class="wide">
<Body size="S">Add the first column to your table.</Body> <Body size="S">
By default, all table columns will automatically be shown.
<br />
You can manually control which columns are included in your table,
and their appearance, by adding them below.
</Body>
</div>
</div> </div>
{/if} {/if}
<div class="columns"> <div class="column">
<div class="column"> <div class="buttons wide">
<div /> <Button secondary icon="Add" on:click={addColumn}>Add column</Button>
<div class="buttons"> <Button secondary quiet on:click={addAllColumns}>
<Button secondary icon="Add" on:click={addColumn}> Add all columns
Add column </Button>
</Button> {#if columns?.length}
<Button secondary quiet on:click={reset}>Reset columns</Button> <Button secondary quiet on:click={reset}>Reset columns</Button>
</div> {/if}
</div> </div>
</div> </div>
</Layout> </Layout>
@ -156,7 +178,7 @@
.column { .column {
gap: var(--spacing-l); gap: var(--spacing-l);
display: grid; display: grid;
grid-template-columns: 20px 1fr 1fr 20px; grid-template-columns: 20px 1fr 1fr auto auto;
align-items: center; align-items: center;
border-radius: var(--border-radius-s); border-radius: var(--border-radius-s);
transition: background-color ease-in-out 130ms; transition: background-color ease-in-out 130ms;
@ -168,6 +190,9 @@
display: grid; display: grid;
place-items: center; place-items: center;
} }
.wide {
grid-column: 2 / -1;
}
.buttons { .buttons {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

29
packages/builder/src/components/design/PropertiesPanel/PropertyControls/ColumnEditor/ColumnEditor.svelte

@ -18,22 +18,30 @@
let boundValue let boundValue
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchemaForDatasource($currentAsset, datasource).schema $: schema = getSchema($currentAsset, datasource)
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: sanitisedValue = getValidColumns(value, options) $: sanitisedValue = getValidColumns(value, options)
$: updateBoundValue(sanitisedValue) $: updateBoundValue(sanitisedValue)
const getSchema = (asset, datasource) => {
const schema = getSchemaForDatasource(asset, datasource).schema
// Don't show ID and rev in tables
if (schema) {
delete schema._id
delete schema._rev
}
return schema
}
const updateBoundValue = value => { const updateBoundValue = value => {
boundValue = cloneDeep(value) boundValue = cloneDeep(value)
} }
const getValidColumns = (columns, options) => { const getValidColumns = (columns, options) => {
// If no columns then default to all columns
if (!Array.isArray(columns) || !columns.length) { if (!Array.isArray(columns) || !columns.length) {
return options.map(col => ({ return []
name: col,
displayName: col,
}))
} }
// We need to account for legacy configs which would just be an array // We need to account for legacy configs which would just be an array
// of strings // of strings
@ -48,17 +56,22 @@
}) })
} }
const open = () => {
updateBoundValue(sanitisedValue)
drawer.show()
}
const save = () => { const save = () => {
dispatch("change", getValidColumns(boundValue, options)) dispatch("change", getValidColumns(boundValue, options))
drawer.hide() drawer.hide()
} }
</script> </script>
<ActionButton on:click={drawer.show}>Configure columns</ActionButton> <ActionButton on:click={open}>Configure columns</ActionButton>
<Drawer bind:this={drawer} title="Table Columns"> <Drawer bind:this={drawer} title="Table Columns">
<svelte:fragment slot="description"> <svelte:fragment slot="description">
Configure the columns in your table. Configure the columns in your table.
</svelte:fragment> </svelte:fragment>
<Button cta slot="buttons" on:click={save}>Save</Button> <Button cta slot="buttons" on:click={save}>Save</Button>
<ColumnDrawer slot="body" bind:columns={boundValue} {options} /> <ColumnDrawer slot="body" bind:columns={boundValue} {options} {schema} />
</Drawer> </Drawer>

13
packages/client/manifest.json

@ -2679,7 +2679,8 @@
"type": "columns", "type": "columns",
"label": "Columns", "label": "Columns",
"key": "columns", "key": "columns",
"dependsOn": "dataProvider" "dependsOn": "dataProvider",
"nested": true
}, },
{ {
"type": "select", "type": "select",
@ -2702,6 +2703,11 @@
"label": "Quiet", "label": "Quiet",
"key": "quiet" "key": "quiet"
}, },
{
"type": "boolean",
"label": "Compact",
"key": "compact"
},
{ {
"type": "boolean", "type": "boolean",
"label": "Show auto columns", "label": "Show auto columns",
@ -2957,6 +2963,11 @@
"label": "Quiet table variant", "label": "Quiet table variant",
"key": "quiet" "key": "quiet"
}, },
{
"type": "boolean",
"label": "Compact",
"key": "compact"
},
{ {
"type": "boolean", "type": "boolean",
"label": "Show auto columns", "label": "Show auto columns",

2
packages/client/src/components/app/blocks/TableBlock.svelte

@ -16,6 +16,7 @@
export let showAutoColumns export let showAutoColumns
export let rowCount export let rowCount
export let quiet export let quiet
export let compact
export let size export let size
export let linkRows export let linkRows
export let linkURL export let linkURL
@ -162,6 +163,7 @@
showAutoColumns, showAutoColumns,
rowCount, rowCount,
quiet, quiet,
compact,
size, size,
linkRows, linkRows,
linkURL, linkURL,

13
packages/client/src/components/app/table/Table.svelte

@ -14,6 +14,7 @@
export let linkURL export let linkURL
export let linkColumn export let linkColumn
export let linkPeek export let linkPeek
export let compact
const component = getContext("component") const component = getContext("component")
const { styleable, getAction, ActionTypes, routeStore } = getContext("sdk") const { styleable, getAction, ActionTypes, routeStore } = getContext("sdk")
@ -72,6 +73,7 @@
order: 0, order: 0,
sortable: false, sortable: false,
divider: true, divider: true,
width: "auto",
} }
} }
@ -84,8 +86,13 @@
if (UnsortableTypes.includes(schema[columnName].type)) { if (UnsortableTypes.includes(schema[columnName].type)) {
newSchema[columnName].sortable = false newSchema[columnName].sortable = false
} }
if (field?.displayName) {
newSchema[columnName].displayName = field?.displayName // Add additional settings like width etc
if (typeof field === "object") {
newSchema[columnName] = {
...newSchema[columnName],
...field,
}
} }
}) })
return newSchema return newSchema
@ -119,12 +126,14 @@
{loading} {loading}
{rowCount} {rowCount}
{quiet} {quiet}
{compact}
{customRenderers} {customRenderers}
allowSelectRows={false} allowSelectRows={false}
allowEditRows={false} allowEditRows={false}
allowEditColumns={false} allowEditColumns={false}
showAutoColumns={true} showAutoColumns={true}
disableSorting disableSorting
autoSortColumns={!columns?.length}
on:sort={onSort} on:sort={onSort}
on:click={onClick} on:click={onClick}
> >

Loading…
Cancel
Save