[Vega] Add ability to open links in Vega in new window (#216200)

## Summary

Fix #70745 

Enables the option to open the clickable mark links of a Vega
Visualization in a new tab.

To achieve this functionality: 
- use the `usermeta.embedOptions.loader` property in the vega chart spec
to pass the `"target": "_blank"` configuration:
```javascript
"usermeta": {
    "embedOptions": {
      "loader": {"target": "_blank"}
    }
  }
``` 

Link opens in a new tab:
![Kapture 2025-03-31 at 20 44
35](https://github.com/user-attachments/assets/bd4f9495-28d7-4a27-89a9-dcb9fb8913dc)


### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>
This commit is contained in:
Andreana Malama 2025-04-14 17:42:32 +03:00 committed by GitHub
parent 530dd5ba20
commit f8e688f881
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 95 additions and 0 deletions

View file

@ -233,6 +233,11 @@ export class VegaBaseView {
return result;
};
const vegaSpec = this._parser.isVegaLite ? this._parser.vlspec : this._parser.spec;
const usermetaLoaderOptions = vegaSpec.usermeta?.embedOptions?.loader;
vegaLoader.options = usermetaLoaderOptions ?? {};
config.loader = vegaLoader;
const vegaLogger = logger(Warn);

View file

@ -27,6 +27,49 @@ signals: [ {
}]
}]}`;
const getLinkTestSpec = (url: string, usermeta?: string) => `
{
$schema: https://vega.github.io/schema/vega-lite/v5.json
data: {
url: {
%context%: true
%timefield%: @timestamp
index: logstash-*
body: {
aggs: {
country_buckets: {
terms: {
field: geo.dest
size: 1
}
}
}
size: 0
}
}
format: {property: "aggregations.country_buckets.buckets"}
}
transform: [
{
"calculate": "'${url}'", "as": "url"
}
]
mark: bar
encoding: {
x: {
field: key
type: nominal
axis: {labelAngle: 0}
}
y: {
field: doc_count
type: quantitative
}
"href": {"field": "url", "type": "nominal"}
}
usermeta: ${usermeta}
}`;
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const { timePicker, visualize, visChart, visEditor, vegaChart } = getPageObjects([
'timePicker',
@ -292,5 +335,52 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
expect(await filterBar.getFilterCount()).to.be(0);
});
});
describe('clickable marks with links', () => {
beforeEach(async () => {
await timePicker.setDefaultAbsoluteRange();
});
const fillSpecAndGo = async (newSpec: string) => {
await vegaChart.fillSpec(newSpec);
await visEditor.clickGo();
const viewContainer = await vegaChart.getViewContainer();
const canvasElement = await viewContainer.findByTagName('canvas');
const { width, height } = await canvasElement.getSize();
await canvasElement.moveMouseTo({
xOffset: Math.round(width / 2),
yOffset: Math.round(height / 2),
});
await canvasElement.click();
};
const getKibanaBaseUrl = async () => {
const currentUrl = await browser.getCurrentUrl();
return currentUrl.substring(0, currentUrl.indexOf('#'));
};
it('should open in a new tab when passing the appropriate usermeta to spec', async () => {
const usermeta = JSON.stringify({ embedOptions: { loader: { target: '_blank' } } });
const spec = getLinkTestSpec(await getKibanaBaseUrl(), usermeta);
await fillSpecAndGo(spec);
const windowHandlers = await browser.getAllWindowHandles();
expect(windowHandlers.length).to.equal(2);
await browser.switchTab(1);
await browser.closeCurrentWindow();
await browser.switchTab(0);
});
it('should open in the same tab without passing the appropriate usermeta to spec', async () => {
const spec = getLinkTestSpec(await getKibanaBaseUrl());
await fillSpecAndGo(spec);
const windowHandlers = await browser.getAllWindowHandles();
expect(windowHandlers.length).to.equal(1);
});
});
});
}