1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2026-02-04 15:38:55 +08:00

#132 node opacity

This commit is contained in:
lana-k
2026-01-22 22:25:36 +01:00
parent 7edc196a02
commit 4e5adc147f
8 changed files with 342 additions and 146 deletions

View File

@@ -319,7 +319,8 @@ export default {
}, },
color: { color: {
type: 'constant', type: 'constant',
value: '#1F77B4' value: '#1F77B4',
opacity: 100
}, },
label: { label: {
source: null, source: null,

View File

@@ -57,10 +57,20 @@
</template> </template>
</Field> </Field>
<Field label="Opacity" fieldContainerClassName="test_node_opacity">
<NumericInput
:value="modelValue.opacity"
:showSlider="true"
:integerOnly="true"
:max="100"
:min="0"
units="%"
@update="updateSettings('opacity', $event)"
/>
</Field>
<Field <Field
v-if=" v-if="modelValue.type === 'map_to' || modelValue.type === 'calculated'"
modelValue.sourceUsage === 'map_to' || modelValue.type === 'calculated'
"
label="Color as" label="Color as"
fieldContainerClassName="test_node_color_as" fieldContainerClassName="test_node_color_as"
> >
@@ -89,6 +99,7 @@
<script> <script>
import { markRaw } from 'vue' import { markRaw } from 'vue'
import { applyPureReactInVue } from 'veaury' import { applyPureReactInVue } from 'veaury'
import NumericInput from 'react-chart-editor/lib/components/widgets/NumericInput'
import Dropdown from 'react-chart-editor/lib/components/widgets/Dropdown' import Dropdown from 'react-chart-editor/lib/components/widgets/Dropdown'
import RadioBlocks from 'react-chart-editor/lib/components/widgets/RadioBlocks' import RadioBlocks from 'react-chart-editor/lib/components/widgets/RadioBlocks'
import ColorscalePicker from 'react-chart-editor/lib/components/widgets/ColorscalePicker' import ColorscalePicker from 'react-chart-editor/lib/components/widgets/ColorscalePicker'
@@ -98,6 +109,7 @@ import 'react-chart-editor/lib/react-chart-editor.css'
export default { export default {
components: { components: {
NumericInput: applyPureReactInVue(NumericInput),
Dropdown: applyPureReactInVue(Dropdown), Dropdown: applyPureReactInVue(Dropdown),
RadioBlocks: applyPureReactInVue(RadioBlocks), RadioBlocks: applyPureReactInVue(RadioBlocks),
Field: applyPureReactInVue(Field), Field: applyPureReactInVue(Field),
@@ -134,19 +146,21 @@ export default {
{ label: 'Map to', value: 'map_to' } { label: 'Map to', value: 'map_to' }
]), ]),
defaultColorSettings: { defaultColorSettings: {
constant: { value: '#1F77B4' }, constant: { value: '#1F77B4', opacity: 100 },
variable: { variable: {
source: null, source: null,
sourceUsage: 'map_to', sourceUsage: 'map_to',
colorscale: null, colorscale: null,
mode: 'categorical', mode: 'categorical',
colorscaleDirection: 'normal' colorscaleDirection: 'normal',
opacity: 100
}, },
calculated: { calculated: {
method: 'degree', method: 'degree',
colorscale: null, colorscale: null,
mode: 'continious', mode: 'continious',
colorscaleDirection: 'normal' colorscaleDirection: 'normal',
opacity: 100
} }
} }
} }

View File

@@ -161,9 +161,14 @@ function getUpdateSizeMethod(graph, sizeSettings) {
} }
} }
function getDirectVariableColorUpdateMethod(source) { function getDirectVariableColorUpdateMethod(source, opacity = 100) {
return attributes => return attributes => {
(attributes.color = tinycolor(attributes.data[source]).toHexString()) const color = tinycolor(attributes.data[source])
const colorOpacity = color.getAlpha()
attributes.color = color
.setAlpha((opacity / 100) * colorOpacity)
.toHex8String()
}
} }
function getUpdateNodeColorMethod(graph, colorSettings) { function getUpdateNodeColorMethod(graph, colorSettings) {
@@ -175,10 +180,16 @@ function getUpdateNodeColorMethod(graph, colorSettings) {
colorscale, colorscale,
colorscaleDirection, colorscaleDirection,
mode, mode,
method method,
opacity
} = colorSettings } = colorSettings
if (type === 'constant') { if (type === 'constant') {
return attributes => (attributes.color = value) const color = tinycolor(value)
const colorOpacity = color.getAlpha()
return attributes =>
(attributes.color = color
.setAlpha((opacity / 100) * colorOpacity)
.toHex8String())
} else if (type === 'variable') { } else if (type === 'variable') {
return sourceUsage === 'map_to' return sourceUsage === 'map_to'
? getColorMethod( ? getColorMethod(
@@ -187,9 +198,10 @@ function getUpdateNodeColorMethod(graph, colorSettings) {
(nodeId, attributes) => attributes.data[source], (nodeId, attributes) => attributes.data[source],
colorscale, colorscale,
colorscaleDirection, colorscaleDirection,
getNodeValueScale getNodeValueScale,
opacity
) )
: getDirectVariableColorUpdateMethod(source) : getDirectVariableColorUpdateMethod(source, opacity)
} else { } else {
return getColorMethod( return getColorMethod(
graph, graph,
@@ -197,7 +209,8 @@ function getUpdateNodeColorMethod(graph, colorSettings) {
nodeId => graph[method](nodeId), nodeId => graph[method](nodeId),
colorscale, colorscale,
colorscaleDirection, colorscaleDirection,
getNodeValueScale getNodeValueScale,
opacity
) )
} }
} }
@@ -244,8 +257,10 @@ function getColorMethod(
sourceGetter, sourceGetter,
selectedColorscale, selectedColorscale,
colorscaleDirection, colorscaleDirection,
valueScaleGetter valueScaleGetter,
opacity = 100
) { ) {
const opacityFactor = opacity / 100
const valueScale = valueScaleGetter(graph, sourceGetter) const valueScale = valueScaleGetter(graph, sourceGetter)
let colorscale = selectedColorscale || DEFAULT_SCALE let colorscale = selectedColorscale || DEFAULT_SCALE
if (colorscaleDirection === 'reversed') { if (colorscaleDirection === 'reversed') {
@@ -261,7 +276,9 @@ function getColorMethod(
) )
return (attributes, nodeId) => { return (attributes, nodeId) => {
const category = sourceGetter(nodeId, attributes) const category = sourceGetter(nodeId, attributes)
attributes.color = colorMap[category] attributes.color = tinycolor(colorMap[category])
.setAlpha(opacityFactor)
.toHex8String()
} }
} else { } else {
const min = valueScale[0] const min = valueScale[0]
@@ -274,14 +291,18 @@ function getColorMethod(
const value = sourceGetter(nodeId, attributes) const value = sourceGetter(nodeId, attributes)
const normalizedValue = (value - min) / (max - min) const normalizedValue = (value - min) / (max - min)
if (isNaN(normalizedValue)) { if (isNaN(normalizedValue)) {
attributes.color = '#000000' attributes.color = tinycolor('#000000')
.setAlpha(opacityFactor)
.toHex8String()
return return
} }
const exactMatch = normalizedColorscale.find( const exactMatch = normalizedColorscale.find(
([value]) => value === normalizedValue ([value]) => value === normalizedValue
) )
if (exactMatch) { if (exactMatch) {
attributes.color = tinycolor(exactMatch[1]).toHexString() attributes.color = tinycolor(exactMatch[1])
.setAlpha(opacityFactor)
.toHex8String()
return return
} }
@@ -305,7 +326,9 @@ function getColorMethod(
r: r0 + interpolationFactor * (r1 - r0), r: r0 + interpolationFactor * (r1 - r0),
g: g0 + interpolationFactor * (g1 - g0), g: g0 + interpolationFactor * (g1 - g0),
b: b0 + interpolationFactor * (b1 - b0) b: b0 + interpolationFactor * (b1 - b0)
}).toHexString() })
.setAlpha(opacityFactor)
.toHex8String()
} }
} }
} }

View File

@@ -1,12 +1,21 @@
export default { export default {
_migrate(installedVersion, inquiries) { _migrate(installedVersion, inquiries) {
if (installedVersion === 1) { if (installedVersion < 2) {
inquiries.forEach(inquire => { inquiries.forEach(inquiry => {
inquire.viewType = 'chart' inquiry.viewType = 'chart'
inquire.viewOptions = inquire.chart inquiry.viewOptions = inquiry.chart
delete inquire.chart delete inquiry.chart
}) })
}
if (installedVersion < 3) {
inquiries.forEach(inquiry => {
if (inquiry.viewType === 'graph') {
inquiry.viewOptions.style.nodes.color.opacity = 100
}
})
}
return inquiries return inquiries
} }
}
} }

View File

@@ -7,7 +7,7 @@ const migrate = migration._migrate
const myInquiriesKey = 'myInquiries' const myInquiriesKey = 'myInquiries'
export default { export default {
version: 2, version: 3,
myInquiriesKey, myInquiriesKey,
getStoredInquiries() { getStoredInquiries() {
let myInquiries = JSON.parse(localStorage.getItem(myInquiriesKey)) let myInquiries = JSON.parse(localStorage.getItem(myInquiriesKey))
@@ -21,7 +21,13 @@ export default {
return [] return []
} }
return (myInquiries && myInquiries.inquiries) || [] if (myInquiries.version === 2) {
myInquiries = migrate(2, myInquiries.inquiries)
this.updateStorage(myInquiries)
return myInquiries
}
return myInquiries.inquiries || []
}, },
duplicateInquiry(baseInquiry) { duplicateInquiry(baseInquiry) {
@@ -82,11 +88,11 @@ export default {
importInquiries() { importInquiries() {
return fu.importFile().then(str => { return fu.importFile().then(str => {
const inquires = this.deserialiseInquiries(str) const inquiries = this.deserialiseInquiries(str)
events.send('inquiry.import', inquires.length) events.send('inquiry.import', inquiries.length)
return inquires return inquiries
}) })
}, },
export(inquiryList, fileName) { export(inquiryList, fileName) {

View File

@@ -24,7 +24,8 @@ const defaultInitOptions = {
}, },
color: { color: {
type: 'constant', type: 'constant',
value: '#1F77B4' value: '#1F77B4',
opacity: 100
}, },
label: { label: {
source: null, source: null,
@@ -404,7 +405,7 @@ describe('GraphEditor', () => {
doc: [ doc: [
'{"type": 0, "node_id": 1, "color": "#ff0000", "points": 5}', '{"type": 0, "node_id": 1, "color": "#ff0000", "points": 5}',
'{"type": 0, "node_id": 2, "color": "#abcdff", "points": 15}', '{"type": 0, "node_id": 2, "color": "#abcdff", "points": 15}',
'{"type": 0, "node_id": 3, "color": "#123456", "points": 10}', '{"type": 0, "node_id": 3, "color": "#12345680", "points": 10}',
'{"type": 1, "source": 2, "target": 3}' '{"type": 1, "source": 2, "target": 3}'
] ]
}, },
@@ -440,11 +441,11 @@ describe('GraphEditor', () => {
// Set constant color // Set constant color
await wrapper await wrapper
.findAllComponents({ name: 'ColorPicker' })[1] .findAllComponents({ name: 'ColorPicker' })[1]
.vm.$emit('colorChange', '#ff00ff') .vm.$emit('colorChange', '#ff00ff80')
expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff') expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff80')
expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff') expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff80')
expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff') expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff80')
// Switch to Variable // Switch to Variable
const variable = wrapper.findAll('.test_node_color .radio-block__option')[1] const variable = wrapper.findAll('.test_node_color .radio-block__option')[1]
@@ -459,27 +460,27 @@ describe('GraphEditor', () => {
await wrapper.findAll('.Select__menu .Select__option')[2].trigger('click') await wrapper.findAll('.Select__menu .Select__option')[2].trigger('click')
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e') expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6eff')
expect(graph.export().nodes[1].attributes.color).to.equal('#bdea75') expect(graph.export().nodes[1].attributes.color).to.equal('#bdea75ff')
expect(graph.export().nodes[2].attributes.color).to.equal('#86d780') expect(graph.export().nodes[2].attributes.color).to.equal('#86d780ff')
// Select Direct mapping // Select Direct mapping
await wrapper await wrapper
.find('.test_node_color_mapping_mode .radio-block__option') .find('.test_node_color_mapping_mode .radio-block__option')
.trigger('click') .trigger('click')
expect(graph.export().nodes[0].attributes.color).to.equal('#ff0000') expect(graph.export().nodes[0].attributes.color).to.equal('#ff0000ff')
expect(graph.export().nodes[1].attributes.color).to.equal('#abcdff') expect(graph.export().nodes[1].attributes.color).to.equal('#abcdffff')
expect(graph.export().nodes[2].attributes.color).to.equal('#123456') expect(graph.export().nodes[2].attributes.color).to.equal('#12345680')
// Switch to Calculated // Switch to Calculated
const calculated = wrapper.findAll( const calculated = wrapper.findAll(
'.test_node_color .radio-block__option' '.test_node_color .radio-block__option'
)[2] )[2]
await calculated.trigger('click') await calculated.trigger('click')
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e') expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6eff')
expect(graph.export().nodes[1].attributes.color).to.equal('#2a4858') expect(graph.export().nodes[1].attributes.color).to.equal('#2a4858ff')
expect(graph.export().nodes[2].attributes.color).to.equal('#2a4858') expect(graph.export().nodes[2].attributes.color).to.equal('#2a4858ff')
await nextTick() await nextTick()
// Choose in-degree // Choose in-degree
@@ -491,45 +492,68 @@ describe('GraphEditor', () => {
await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click') await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click')
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e') expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6eff')
expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6e') expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6eff')
expect(graph.export().nodes[2].attributes.color).to.equal('#2a4858') expect(graph.export().nodes[2].attributes.color).to.equal('#2a4858ff')
await nextTick()
// Set another opacity for calculated color
let opacityInput = wrapper.find(
'.test_node_opacity input.numeric-input__number'
)
await opacityInput.setValue(50)
opacityInput.wrapperElement.dispatchEvent(
new Event('blur', { bubbles: true })
)
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e80')
expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6e80')
expect(graph.export().nodes[2].attributes.color).to.equal('#2a485880')
await nextTick() await nextTick()
// Set Color as to Categorical // Set Color as to Categorical
await wrapper await wrapper
.findAll('.test_node_color_as .radio-block__option')[1] .findAll('.test_node_color_as .radio-block__option')[1]
.trigger('click') .trigger('click')
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e') expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e80')
expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6e') expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6e80')
expect(graph.export().nodes[2].attributes.color).to.equal('#bdea75') expect(graph.export().nodes[2].attributes.color).to.equal('#bdea7580')
await nextTick() await nextTick()
// Change colorscale direction // Change colorscale direction
await wrapper await wrapper
.findAll('.test_node_color_colorscale_direction .radio-block__option')[1] .findAll('.test_node_color_colorscale_direction .radio-block__option')[1]
.trigger('click') .trigger('click')
expect(graph.export().nodes[0].attributes.color).to.equal('#2a4858') expect(graph.export().nodes[0].attributes.color).to.equal('#2a485880')
expect(graph.export().nodes[1].attributes.color).to.equal('#2a4858') expect(graph.export().nodes[1].attributes.color).to.equal('#2a485880')
expect(graph.export().nodes[2].attributes.color).to.equal('#1f5f70') expect(graph.export().nodes[2].attributes.color).to.equal('#1f5f7080')
await nextTick() await nextTick()
// Switch to Variable // Switch to Variable
await variable.trigger('click') await variable.trigger('click')
// The latest settings from variable mode are applied // The latest settings from variable mode are applied
expect(graph.export().nodes[0].attributes.color).to.equal('#ff0000') expect(graph.export().nodes[0].attributes.color).to.equal('#ff0000ff')
expect(graph.export().nodes[1].attributes.color).to.equal('#abcdff') expect(graph.export().nodes[1].attributes.color).to.equal('#abcdffff')
expect(graph.export().nodes[2].attributes.color).to.equal('#123456') expect(graph.export().nodes[2].attributes.color).to.equal('#12345680')
// Switch to Constant // Switch to Constant
const constant = wrapper.findAll('.test_node_color .radio-block__option')[0] const constant = wrapper.findAll('.test_node_color .radio-block__option')[0]
await constant.trigger('click') await constant.trigger('click')
// The latest settings from constant mode are applied // The latest settings from constant mode are applied
expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff') expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff80')
expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff') expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff80')
expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff') expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff80')
// Set another opacity for constant color
await opacityInput.setValue(50)
opacityInput.wrapperElement.dispatchEvent(
new Event('blur', { bubbles: true })
)
expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff40')
expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff40')
expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff40')
await nextTick()
wrapper.unmount() wrapper.unmount()
}) })
@@ -840,26 +864,26 @@ describe('GraphEditor', () => {
) )
await wrapper.findAll('.Select__menu .Select__option')[5].trigger('click') await wrapper.findAll('.Select__menu .Select__option')[5].trigger('click')
expect(graph.export().edges[0].attributes.color).to.equal('#fafa6e') expect(graph.export().edges[0].attributes.color).to.equal('#fafa6eff')
expect(graph.export().edges[1].attributes.color).to.equal('#bdea75') expect(graph.export().edges[1].attributes.color).to.equal('#bdea75ff')
expect(graph.export().edges[2].attributes.color).to.equal('#86d780') expect(graph.export().edges[2].attributes.color).to.equal('#86d780ff')
// Set Color as to Continious // Set Color as to Continious
await wrapper await wrapper
.findAll('.test_edge_color_as .radio-block__option')[0] .findAll('.test_edge_color_as .radio-block__option')[0]
.trigger('click') .trigger('click')
expect(graph.export().edges[0].attributes.color).to.equal('#fafa6e') expect(graph.export().edges[0].attributes.color).to.equal('#fafa6eff')
expect(graph.export().edges[1].attributes.color).to.equal('#39b48d') expect(graph.export().edges[1].attributes.color).to.equal('#39b48dff')
expect(graph.export().edges[2].attributes.color).to.equal('#2a4858') expect(graph.export().edges[2].attributes.color).to.equal('#2a4858ff')
await nextTick() await nextTick()
// Change colorscale direction // Change colorscale direction
await wrapper await wrapper
.findAll('.test_edge_color_colorscale_direction .radio-block__option')[1] .findAll('.test_edge_color_colorscale_direction .radio-block__option')[1]
.trigger('click') .trigger('click')
expect(graph.export().edges[0].attributes.color).to.equal('#2a4858') expect(graph.export().edges[0].attributes.color).to.equal('#2a4858ff')
expect(graph.export().edges[1].attributes.color).to.equal('#139f8e') expect(graph.export().edges[1].attributes.color).to.equal('#139f8eff')
expect(graph.export().edges[2].attributes.color).to.equal('#fafa6e') expect(graph.export().edges[2].attributes.color).to.equal('#fafa6eff')
await nextTick() await nextTick()
// Clear color source // Clear color source
@@ -883,9 +907,9 @@ describe('GraphEditor', () => {
.find('.test_edge_color_mapping_mode .radio-block__option') .find('.test_edge_color_mapping_mode .radio-block__option')
.trigger('click') .trigger('click')
expect(graph.export().edges[0].attributes.color).to.equal('#ff0000') expect(graph.export().edges[0].attributes.color).to.equal('#ff0000ff')
expect(graph.export().edges[1].attributes.color).to.equal('#abcdff') expect(graph.export().edges[1].attributes.color).to.equal('#abcdffff')
expect(graph.export().edges[2].attributes.color).to.equal('#123456') expect(graph.export().edges[2].attributes.color).to.equal('#123456ff')
// Switch to Constant // Switch to Constant
const constant = wrapper.findAll('.test_edge_color .radio-block__option')[0] const constant = wrapper.findAll('.test_edge_color .radio-block__option')[0]

View File

@@ -533,7 +533,8 @@ describe('graphHelper.js', () => {
graphHelper.updateNodes(graph, { graphHelper.updateNodes(graph, {
color: { color: {
type: 'constant', type: 'constant',
value: '#a1b8c3' value: '#a1b8c380',
opacity: 50
} }
}) })
expect(graph.export().nodes).to.eql([ expect(graph.export().nodes).to.eql([
@@ -541,14 +542,14 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1 }, data: { type: 0, node_id: 1 },
color: '#a1b8c3' color: '#a1b8c340'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2 }, data: { type: 0, node_id: 2 },
color: '#a1b8c3' color: '#a1b8c340'
} }
} }
]) ])
@@ -592,7 +593,7 @@ describe('graphHelper.js', () => {
doc: [ doc: [
'{"type": 0, "node_id": 1, "color": "red", "points": 5}', '{"type": 0, "node_id": 1, "color": "red", "points": 5}',
'{"type": 0, "node_id": 2, "color": "#abcdff", "points": 15}', '{"type": 0, "node_id": 2, "color": "#abcdff", "points": 15}',
'{"type": 0, "node_id": 3, "color": "#123456", "points": 10}' '{"type": 0, "node_id": 3, "color": "#12345680", "points": 10}'
] ]
} }
const graph = new Graph() const graph = new Graph()
@@ -609,7 +610,8 @@ describe('graphHelper.js', () => {
color: { color: {
type: 'variable', type: 'variable',
source: 'color', source: 'color',
sourceUsage: 'direct' sourceUsage: 'direct',
opacity: 50
} }
}) })
expect(graph.export().nodes).to.eql([ expect(graph.export().nodes).to.eql([
@@ -617,21 +619,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1, color: 'red', points: 5 }, data: { type: 0, node_id: 1, color: 'red', points: 5 },
color: '#ff0000' color: '#ff000080'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2, color: '#abcdff', points: 15 }, data: { type: 0, node_id: 2, color: '#abcdff', points: 15 },
color: '#abcdff' color: '#abcdff80'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3, color: '#123456', points: 10 }, data: { type: 0, node_id: 3, color: '#12345680', points: 10 },
color: '#123456' color: '#12345640'
} }
} }
]) ])
@@ -644,7 +646,8 @@ describe('graphHelper.js', () => {
sourceUsage: 'map_to', sourceUsage: 'map_to',
colorscale, colorscale,
mode: 'categorical', mode: 'categorical',
colorscaleDirection: 'normal' colorscaleDirection: 'normal',
opacity: 50
} }
}) })
expect(graph.export().nodes).to.eql([ expect(graph.export().nodes).to.eql([
@@ -652,21 +655,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1, color: 'red', points: 5 }, data: { type: 0, node_id: 1, color: 'red', points: 5 },
color: '#aaaaff' color: '#aaaaff80'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2, color: '#abcdff', points: 15 }, data: { type: 0, node_id: 2, color: '#abcdff', points: 15 },
color: '#6666ff' color: '#6666ff80'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3, color: '#123456', points: 10 }, data: { type: 0, node_id: 3, color: '#12345680', points: 10 },
color: '#8888ff' color: '#8888ff80'
} }
} }
]) ])
@@ -678,7 +681,8 @@ describe('graphHelper.js', () => {
sourceUsage: 'map_to', sourceUsage: 'map_to',
colorscale, colorscale,
mode: 'categorical', mode: 'categorical',
colorscaleDirection: 'reversed' colorscaleDirection: 'reversed',
opacity: 50
} }
}) })
@@ -687,21 +691,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1, color: 'red', points: 5 }, data: { type: 0, node_id: 1, color: 'red', points: 5 },
color: '#0000ff' color: '#0000ff80'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2, color: '#abcdff', points: 15 }, data: { type: 0, node_id: 2, color: '#abcdff', points: 15 },
color: '#6666ff' color: '#6666ff80'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3, color: '#123456', points: 10 }, data: { type: 0, node_id: 3, color: '#12345680', points: 10 },
color: '#4444ff' color: '#4444ff80'
} }
} }
]) ])
@@ -713,7 +717,8 @@ describe('graphHelper.js', () => {
sourceUsage: 'map_to', sourceUsage: 'map_to',
colorscale, colorscale,
mode: 'continious', mode: 'continious',
colorscaleDirection: 'normal' colorscaleDirection: 'normal',
opacity: 50
} }
}) })
expect(graph.export().nodes).to.eql([ expect(graph.export().nodes).to.eql([
@@ -721,21 +726,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1, color: 'red', points: 5 }, data: { type: 0, node_id: 1, color: 'red', points: 5 },
color: '#aaaaff' color: '#aaaaff80'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2, color: '#abcdff', points: 15 }, data: { type: 0, node_id: 2, color: '#abcdff', points: 15 },
color: '#0000ff' color: '#0000ff80'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3, color: '#123456', points: 10 }, data: { type: 0, node_id: 3, color: '#12345680', points: 10 },
color: '#6666ff' color: '#6666ff80'
} }
} }
]) ])
@@ -747,7 +752,8 @@ describe('graphHelper.js', () => {
sourceUsage: 'map_to', sourceUsage: 'map_to',
colorscale, colorscale,
mode: 'continious', mode: 'continious',
colorscaleDirection: 'reversed' colorscaleDirection: 'reversed',
opacity: 50
} }
}) })
@@ -756,21 +762,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1, color: 'red', points: 5 }, data: { type: 0, node_id: 1, color: 'red', points: 5 },
color: '#0000ff' color: '#0000ff80'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2, color: '#abcdff', points: 15 }, data: { type: 0, node_id: 2, color: '#abcdff', points: 15 },
color: '#aaaaff' color: '#aaaaff80'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3, color: '#123456', points: 10 }, data: { type: 0, node_id: 3, color: '#12345680', points: 10 },
color: '#6666ff' color: '#6666ff80'
} }
} }
]) ])
@@ -782,7 +788,8 @@ describe('graphHelper.js', () => {
sourceUsage: 'map_to', sourceUsage: 'map_to',
colorscale: ['#aaaaff', '#0000ff'], colorscale: ['#aaaaff', '#0000ff'],
mode: 'continious', mode: 'continious',
colorscaleDirection: 'normal' colorscaleDirection: 'normal',
opacity: 50
} }
}) })
@@ -791,21 +798,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1, color: 'red', points: 5 }, data: { type: 0, node_id: 1, color: 'red', points: 5 },
color: '#aaaaff' color: '#aaaaff80'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2, color: '#abcdff', points: 15 }, data: { type: 0, node_id: 2, color: '#abcdff', points: 15 },
color: '#0000ff' color: '#0000ff80'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3, color: '#123456', points: 10 }, data: { type: 0, node_id: 3, color: '#12345680', points: 10 },
color: '#5555ff' color: '#5555ff80'
} }
} }
]) ])
@@ -834,7 +841,8 @@ describe('graphHelper.js', () => {
source: 'country', source: 'country',
sourceUsage: 'map_to', sourceUsage: 'map_to',
mode: 'continious', mode: 'continious',
colorscaleDirection: 'normal' colorscaleDirection: 'normal',
opacity: 100
} }
}) })
expect(graph.export().nodes).to.eql([ expect(graph.export().nodes).to.eql([
@@ -842,14 +850,14 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1, country: 'NL' }, data: { type: 0, node_id: 1, country: 'NL' },
color: '#000000' color: '#000000ff'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2, country: 'GB' }, data: { type: 0, node_id: 2, country: 'GB' },
color: '#000000' color: '#000000ff'
} }
} }
]) ])
@@ -887,15 +895,15 @@ describe('graphHelper.js', () => {
expect(graph.export().edges.map(edge => edge.attributes)).to.eql([ expect(graph.export().edges.map(edge => edge.attributes)).to.eql([
{ {
data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 }, data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 },
color: '#ff0000' color: '#ff0000ff'
}, },
{ {
data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 }, data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 },
color: '#ff0000' color: '#ff0000ff'
}, },
{ {
data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 }, data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 },
color: '#ff0000' color: '#ff0000ff'
} }
]) ])
@@ -914,15 +922,15 @@ describe('graphHelper.js', () => {
expect(graph.export().edges.map(edge => edge.attributes)).to.eql([ expect(graph.export().edges.map(edge => edge.attributes)).to.eql([
{ {
data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 }, data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 },
color: '#aaaaff' color: '#aaaaffff'
}, },
{ {
data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 }, data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 },
color: '#6666ff' color: '#6666ffff'
}, },
{ {
data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 }, data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 },
color: '#8888ff' color: '#8888ffff'
} }
]) ])
@@ -940,15 +948,15 @@ describe('graphHelper.js', () => {
expect(graph.export().edges.map(edge => edge.attributes)).to.eql([ expect(graph.export().edges.map(edge => edge.attributes)).to.eql([
{ {
data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 }, data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 },
color: '#0000ff' color: '#0000ffff'
}, },
{ {
data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 }, data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 },
color: '#6666ff' color: '#6666ffff'
}, },
{ {
data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 }, data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 },
color: '#4444ff' color: '#4444ffff'
} }
]) ])
@@ -966,15 +974,15 @@ describe('graphHelper.js', () => {
expect(graph.export().edges.map(edge => edge.attributes)).to.eql([ expect(graph.export().edges.map(edge => edge.attributes)).to.eql([
{ {
data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 }, data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 },
color: '#aaaaff' color: '#aaaaffff'
}, },
{ {
data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 }, data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 },
color: '#0000ff' color: '#0000ffff'
}, },
{ {
data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 }, data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 },
color: '#6666ff' color: '#6666ffff'
} }
]) ])
@@ -992,15 +1000,15 @@ describe('graphHelper.js', () => {
expect(graph.export().edges.map(edge => edge.attributes)).to.eql([ expect(graph.export().edges.map(edge => edge.attributes)).to.eql([
{ {
data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 }, data: { type: 1, source: 1, target: 2, color: 'red', weight: 5 },
color: '#0000ff' color: '#0000ffff'
}, },
{ {
data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 }, data: { type: 1, source: 1, target: 3, color: 'red', weight: 15 },
color: '#aaaaff' color: '#aaaaffff'
}, },
{ {
data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 }, data: { type: 1, source: 2, target: 3, color: 'red', weight: 10 },
color: '#6666ff' color: '#6666ffff'
} }
]) ])
}) })
@@ -1035,7 +1043,8 @@ describe('graphHelper.js', () => {
method: 'degree', method: 'degree',
colorscale, colorscale,
mode: 'categorical', mode: 'categorical',
colorscaleDirection: 'normal' colorscaleDirection: 'normal',
opacity: 50
} }
}) })
expect(graph.export().nodes).to.eql([ expect(graph.export().nodes).to.eql([
@@ -1043,21 +1052,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1 }, data: { type: 0, node_id: 1 },
color: '#aaaaff' color: '#aaaaff80'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2 }, data: { type: 0, node_id: 2 },
color: '#8888ff' color: '#8888ff80'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3 }, data: { type: 0, node_id: 3 },
color: '#8888ff' color: '#8888ff80'
} }
} }
]) ])
@@ -1068,7 +1077,8 @@ describe('graphHelper.js', () => {
method: 'outDegree', method: 'outDegree',
colorscale, colorscale,
mode: 'categorical', mode: 'categorical',
colorscaleDirection: 'reversed' colorscaleDirection: 'reversed',
opacity: 50
} }
}) })
@@ -1077,21 +1087,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1 }, data: { type: 0, node_id: 1 },
color: '#0000ff' color: '#0000ff80'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2 }, data: { type: 0, node_id: 2 },
color: '#4444ff' color: '#4444ff80'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3 }, data: { type: 0, node_id: 3 },
color: '#0000ff' color: '#0000ff80'
} }
} }
]) ])
@@ -1102,7 +1112,8 @@ describe('graphHelper.js', () => {
method: 'degree', method: 'degree',
colorscale, colorscale,
mode: 'continious', mode: 'continious',
colorscaleDirection: 'normal' colorscaleDirection: 'normal',
opacity: 100
} }
}) })
expect(graph.export().nodes).to.eql([ expect(graph.export().nodes).to.eql([
@@ -1110,21 +1121,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1 }, data: { type: 0, node_id: 1 },
color: '#aaaaff' color: '#aaaaffff'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2 }, data: { type: 0, node_id: 2 },
color: '#0000ff' color: '#0000ffff'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3 }, data: { type: 0, node_id: 3 },
color: '#0000ff' color: '#0000ffff'
} }
} }
]) ])
@@ -1135,7 +1146,8 @@ describe('graphHelper.js', () => {
method: 'degree', method: 'degree',
colorscale, colorscale,
mode: 'continious', mode: 'continious',
colorscaleDirection: 'reversed' colorscaleDirection: 'reversed',
opacity: 100
} }
}) })
@@ -1144,21 +1156,21 @@ describe('graphHelper.js', () => {
key: '1', key: '1',
attributes: { attributes: {
data: { type: 0, node_id: 1 }, data: { type: 0, node_id: 1 },
color: '#0000ff' color: '#0000ffff'
} }
}, },
{ {
key: '2', key: '2',
attributes: { attributes: {
data: { type: 0, node_id: 2 }, data: { type: 0, node_id: 2 },
color: '#aaaaff' color: '#aaaaffff'
} }
}, },
{ {
key: '3', key: '3',
attributes: { attributes: {
data: { type: 0, node_id: 3 }, data: { type: 0, node_id: 3 },
color: '#aaaaff' color: '#aaaaffff'
} }
} }
]) ])

View File

@@ -18,7 +18,7 @@ describe('storedInquiries.js', () => {
expect(inquiries).to.eql([]) expect(inquiries).to.eql([])
}) })
it('getStoredInquiries migrate and returns inquiries of v1', () => { it('getStoredInquiries migrates and returns inquiries of v1', () => {
localStorage.setItem( localStorage.setItem(
'myQueries', 'myQueries',
JSON.stringify([ JSON.stringify([
@@ -55,6 +55,113 @@ describe('storedInquiries.js', () => {
]) ])
}) })
it('getStoredInquiries migrates and returns inquiries of v2', () => {
localStorage.setItem(
'myInquiries',
JSON.stringify({
version: 2,
inquiries: [
{
id: 'Xh1Hc9v7P3mRPZVM59QiC',
query: 'SELECT * from doc',
viewType: 'graph',
viewOptions: {
structure: {
nodeId: 'node_id',
objectType: 'object_type',
edgeSource: 'source',
edgeTarget: 'target'
},
style: {
backgroundColor: 'white',
nodes: {
size: { type: 'constant', value: 10 },
color: {
type: 'calculated',
method: 'degree',
colorscale: null,
mode: 'continious',
colorscaleDirection: 'reversed'
},
label: { source: 'label', color: '#444444' }
},
edges: {
showDirection: true,
size: { type: 'constant', value: 2 },
color: { type: 'constant', value: '#a2b1c6' },
label: { source: null, color: '#a2b1c6' }
}
},
layout: { type: 'circular', options: null }
},
name: 'student graph',
updatedAt: '2026-01-19T21:49:40.708Z',
createdAt: '2026-01-19T21:46:13.899Z'
},
{
id: 'Yh1Hc9v7P3mRPZVM59QiD',
query: 'SELECT * from test',
viewType: 'chart',
viewOptions: 'some chart view options',
name: 'student chart',
updatedAt: '2026-01-19T21:49:40.708Z',
createdAt: '2026-01-19T21:46:13.899Z'
}
]
})
)
const inquiries = storedInquiries.getStoredInquiries()
expect(inquiries).to.eql([
{
id: 'Xh1Hc9v7P3mRPZVM59QiC',
query: 'SELECT * from doc',
viewType: 'graph',
viewOptions: {
structure: {
nodeId: 'node_id',
objectType: 'object_type',
edgeSource: 'source',
edgeTarget: 'target'
},
style: {
backgroundColor: 'white',
nodes: {
size: { type: 'constant', value: 10 },
color: {
type: 'calculated',
method: 'degree',
colorscale: null,
mode: 'continious',
colorscaleDirection: 'reversed',
opacity: 100
},
label: { source: 'label', color: '#444444' }
},
edges: {
showDirection: true,
size: { type: 'constant', value: 2 },
color: { type: 'constant', value: '#a2b1c6' },
label: { source: null, color: '#a2b1c6' }
}
},
layout: { type: 'circular', options: null }
},
name: 'student graph',
updatedAt: '2026-01-19T21:49:40.708Z',
createdAt: '2026-01-19T21:46:13.899Z'
},
{
id: 'Yh1Hc9v7P3mRPZVM59QiD',
query: 'SELECT * from test',
viewType: 'chart',
viewOptions: 'some chart view options',
name: 'student chart',
updatedAt: '2026-01-19T21:49:40.708Z',
createdAt: '2026-01-19T21:46:13.899Z'
}
])
})
it('updateStorage and getStoredInquiries', () => { it('updateStorage and getStoredInquiries', () => {
const data = [{ id: 1 }, { id: 2 }] const data = [{ id: 1 }, { id: 2 }]
storedInquiries.updateStorage(data) storedInquiries.updateStorage(data)
@@ -136,7 +243,7 @@ describe('storedInquiries.js', () => {
const str = storedInquiries.serialiseInquiries(inquiryList) const str = storedInquiries.serialiseInquiries(inquiryList)
const parsedJson = JSON.parse(str) const parsedJson = JSON.parse(str)
expect(parsedJson.version).to.equal(2) expect(parsedJson.version).to.equal(3)
expect(parsedJson.inquiries).to.have.lengthOf(2) expect(parsedJson.inquiries).to.have.lengthOf(2)
expect(parsedJson.inquiries[1]).to.eql(inquiryList[1]) expect(parsedJson.inquiries[1]).to.eql(inquiryList[1])
expect(parsedJson.inquiries[0]).to.eql({ expect(parsedJson.inquiries[0]).to.eql({
@@ -215,7 +322,7 @@ describe('storedInquiries.js', () => {
it('deserialiseInquiries generates new id to avoid duplication', () => { it('deserialiseInquiries generates new id to avoid duplication', () => {
storedInquiries.updateStorage([{ id: 1 }]) storedInquiries.updateStorage([{ id: 1 }])
const str = `{ const str = `{
"version": 2, "version": 3,
"inquiries": [ "inquiries": [
{ {
"id": 1, "id": 1,
@@ -275,7 +382,7 @@ describe('storedInquiries.js', () => {
it('importInquiries', async () => { it('importInquiries', async () => {
const str = `{ const str = `{
"version": 2, "version": 3,
"inquiries": [{ "inquiries": [{
"id": 1, "id": 1,
"name": "foo", "name": "foo",
@@ -327,7 +434,7 @@ describe('storedInquiries.js', () => {
it('readPredefinedInquiries', async () => { it('readPredefinedInquiries', async () => {
const str = `{ const str = `{
"version": 2, "version": 3,
"inquiries": [ "inquiries": [
{ {
"id": 1, "id": 1,