feat: add viewDataToggled and toggleFocus (#1711)

* feat: add viewDataToggled and toggleFocus

* fix: add imperative handle focus

* fix: check blurCallback properly

* feat: add support data view

* feat: add setOnBlurHandler

* feat: update viz api to include ext

* fix: make sure viewdata returns correctly

* chore: add getRenderstate

* chore: clean out comment

Co-authored-by: Christian Veinfors <cvs@qlik.com>

* chore: shuffle support func into Cell

* chore: throw error in support when in view data

* chore: store support object for original chart

* feat: add viewDataToggled and toggleFocus

* fix: add imperative handle focus

* fix: check blurCallback properly

* feat: add support data view

* feat: add setOnBlurHandler

* feat: update viz api to include ext

* fix: make sure viewdata returns correctly

* chore: add getRenderstate

* chore: clean out comment

Co-authored-by: Christian Veinfors <cvs@qlik.com>

* chore: shuffle support func into Cell

* chore: throw error in support when in view data

* chore: store support object for original chart

* chore: fix test

* fix: rename to getExtensionDefinition

* fix: check if current is not undefined

* chore: store outer layout

---------

Co-authored-by: caele <tsm@qlik.com>
Co-authored-by: Christian Veinfors <cvs@qlik.com>
This commit is contained in:
Linh Nihlgård
2025-04-28 21:22:58 +02:00
committed by GitHub
parent abe2a43ef2
commit 63850fe28f
7 changed files with 114 additions and 39 deletions

View File

@@ -18,6 +18,7 @@ describe('viz', () => {
let setSnOptions;
let setSnContext;
let setSnPlugins;
let getExtensionDefinition;
let setModel;
let takeSnapshot;
let exportImage;
@@ -33,6 +34,7 @@ describe('viz', () => {
setSnOptions = jest.fn();
setSnContext = jest.fn();
setSnPlugins = jest.fn();
getExtensionDefinition = jest.fn();
setModel = jest.fn();
takeSnapshot = jest.fn();
exportImage = jest.fn();
@@ -44,6 +46,7 @@ describe('viz', () => {
setSnOptions,
setSnContext,
setSnPlugins,
getExtensionDefinition,
setModel,
takeSnapshot,
exportImage,

View File

@@ -39,6 +39,17 @@ const CellBody = {
className: 'njs-cell-body',
};
function support(prop, supportObject, layout) {
const value = supportObject[prop];
if (typeof value === 'function') {
return value.call(null, layout);
}
if (typeof value === 'boolean') {
return value;
}
return false;
}
const initialState = (err) => ({
loading: false,
loaded: false,
@@ -460,7 +471,6 @@ const Cell = forwardRef(
return () => {};
}, [types, state.sn, model, selections, layout, appLayout, language]);
// Long running query
useEffect(() => {
if (!validating) {
@@ -477,6 +487,19 @@ const Cell = forwardRef(
getQae() {
return state.sn.generator.qae;
},
getExtensionDefinition() {
return state.sn.generator.definition.ext;
},
// allow input of supportObject ot override when flipped to table
support(type, supportObject, outerLayout) {
if (layout && state.loaded && !state.error) {
const suppObj = supportObject || state.sn.generator.definition.ext?.support;
if (suppObj) {
return support(type, suppObj, outerLayout || layout);
}
}
return false;
},
toggleFocus(active) {
if (typeof state.sn.component.focus === 'function') {
if (active) {
@@ -486,6 +509,9 @@ const Cell = forwardRef(
}
}
},
setOnBlurHandler(cb) {
focusHandler.current.blurCallback = cb;
},
setSnOptions,
setSnPlugins,
setModel,

View File

@@ -94,7 +94,7 @@ export default function boot({ app, context }) {
{
toggleFocusOfCells(cellIdToFocus) {
Object.keys(cells).forEach((i) => {
cells[i].current.toggleFocus(i === cellIdToFocus);
cells[i].current?.toggleFocus(i === cellIdToFocus);
});
},
cells,

View File

@@ -25,6 +25,10 @@ export default function viz({
let onMount = null;
let onRenderResolve = null;
let viewDataObjectId;
let originalExtensionDef;
let originalLayout;
let successfulRender = false;
const mounted = new Promise((resolve) => {
onMount = resolve;
});
@@ -37,6 +41,7 @@ export default function viz({
override?.(); // from options.onInitialRender
onRenderResolve(); // internal promise in viz to wait for render
onRender(); // from RenderConfig
successfulRender = true;
};
let initialSnOptions = {};
@@ -194,15 +199,26 @@ export default function viz({
});
newModel = await halo.app.createSessionObject(propertyTree.qProperty);
viewDataObjectId = newModel.id;
originalExtensionDef = cellRef.current.getExtensionDefinition();
originalLayout = await model.getLayout();
} else if (viewDataObjectId && showDataView !== true) {
newModel = model;
await halo.app.destroySessionObject(viewDataObjectId);
viewDataObjectId = undefined;
originalExtensionDef = undefined;
originalLayout = undefined;
}
if (newModel) {
cellRef.current.setModel(newModel);
}
},
/**
* Whether or not the chart has the data view toggled on.
* @type {boolean}
*/
get viewDataToggled() {
return viewDataObjectId !== undefined;
},
/**
* Listens to custom events from inside the visualization. See useEmitter
* @param {string} eventName Event name to listen to
@@ -237,6 +253,25 @@ export default function viz({
await rendered;
return cellRef.current.takeSnapshot();
},
// ===== undocumented experimental API - use at own risk ======
/**
* valid types: viewData, cssScaling, snapshot, exportData, exploration
* questionable types: supportRefresh, quickMobile, fullscreen
* deprecated?: sharing
*
*/
support(type) {
if (mountedReference && successfulRender) {
return cellRef.current.support(type, originalExtensionDef?.support, originalLayout);
}
return false;
},
toggleFocus(focus) {
cellRef.current.toggleFocus(focus);
},
setOnBlurHandler(cb) {
cellRef.current.setOnBlurHandler(cb);
},
// ===== unexposed experimental API - use at own risk ======
__DO_NOT_USE__: {
mount(element) {

View File

@@ -1413,6 +1413,10 @@
}
]
},
"viewDataToggled": {
"description": "Whether or not the chart has the data view toggled on.",
"type": "boolean"
},
"addListener": {
"description": "Listens to custom events from inside the visualization. See useEmitter",
"kind": "function",
@@ -1723,6 +1727,33 @@
}
}
},
"Plugin": {
"description": "An object literal containing meta information about the plugin and a function containing the plugin implementation.",
"stability": "experimental",
"availability": {
"since": "1.2.0"
},
"kind": "interface",
"entries": {
"info": {
"description": "Object that can hold various meta info about the plugin",
"kind": "object",
"entries": {
"name": {
"description": "The name of the plugin",
"type": "string"
}
}
},
"fn": {
"description": "The implementation of the plugin. Input and return value is up to the plugin implementation to decide based on its purpose.",
"type": "function"
}
},
"examples": [
"const plugin = {\n info: {\n name: \"example-plugin\",\n type: \"meta-type\",\n },\n fn: () => {\n // Plugin implementation goes here\n }\n};"
]
},
"Field": {
"kind": "alias",
"items": {
@@ -1874,33 +1905,6 @@
}
}
},
"Plugin": {
"description": "An object literal containing meta information about the plugin and a function containing the plugin implementation.",
"stability": "experimental",
"availability": {
"since": "1.2.0"
},
"kind": "interface",
"entries": {
"info": {
"description": "Object that can hold various meta info about the plugin",
"kind": "object",
"entries": {
"name": {
"description": "The name of the plugin",
"type": "string"
}
}
},
"fn": {
"description": "The implementation of the plugin. Input and return value is up to the plugin implementation to decide based on its purpose.",
"type": "function"
}
},
"examples": [
"const plugin = {\n info: {\n name: \"example-plugin\",\n type: \"meta-type\",\n },\n fn: () => {\n // Plugin implementation goes here\n }\n};"
]
},
"LoadType": {
"kind": "interface",
"params": [

View File

@@ -453,6 +453,8 @@ declare namespace stardust {
*/
toggleDataView(showDataView?: boolean): void;
viewDataToggled: boolean;
/**
* Listens to custom events from inside the visualization. See useEmitter
* @param eventName Event name to listen to
@@ -556,6 +558,16 @@ declare namespace stardust {
}
/**
* An object literal containing meta information about the plugin and a function containing the plugin implementation.
*/
interface Plugin {
info: {
name: string;
};
fn: ()=>void;
}
type Field = string | qix.NxDimension | qix.NxMeasure | stardust.LibraryField;
/**
@@ -597,16 +609,6 @@ declare namespace stardust {
type: "dimension" | "measure";
}
/**
* An object literal containing meta information about the plugin and a function containing the plugin implementation.
*/
interface Plugin {
info: {
name: string;
};
fn: ()=>void;
}
interface LoadType {
(type: {
name: string;

View File

@@ -216,6 +216,11 @@ function createWithHooks(generator, opts, galaxy) {
return generator.component.runMenu(this, menu, event, menuBuilder);
},
focus() {
const ref = generator.component.getImperativeHandle(this);
if (ref && typeof ref.focus === 'function') {
ref.focus();
return;
}
generator.component.focus(this);
},
blur() {