Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for HYPERLINK (handsontable) #10314

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions examples/next/docs/js/hyperlink-formula/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
dist
build
.cache
.env
.eslintcache
package-lock.json
28 changes: 28 additions & 0 deletions examples/next/docs/js/hyperlink-formula/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Copyright (c) HANDSONCODE sp. z o. o.

HANDSONTABLE is a software distributed by HANDSONCODE sp. z o. o.,
a Polish corporation, based in Gdynia, Poland, at 96/98 Aleja Zwycięstwa,
registered with the National Court Register under number 538651,
EU tax ID number: PL5862294002, share capital: PLN 62,800.00.

This software is protected by applicable copyright laws, including
international treaties, and dual-licensed – depending on whether
your use is intended for or may result in commercial advantage
or monetary compensation (commercial purposes), or not.

If your use involves only such purposes as research, private study,
evaluation and the like, you agree to be bound by the terms included
in the "handsontable-non-commercial-license.pdf" file, available
in the main directory of this software repository.

By installing, copying, or otherwise using this software for
commercial purposes, you agree to be bound by the terms included
in the "handsontable-general-terms.pdf" file, available in the main
directory of this software repository.

HANDSONCODE PROVIDES THIS SOFTWARE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. IN NO EVENT
AND UNDER NO LEGAL THEORY, SHALL HANDSONCODE BE LIABLE
TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING
FROM USE OR INABILITY TO USE THIS SOFTWARE.
39 changes: 39 additions & 0 deletions examples/next/docs/js/hyperlink-formula/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# {{ YOUR TITLE GOES HERE }}

## Description

{{ YOUR DESCRIPTION GOES HERE }}

## How to run this example

### Installation

Call `npm install` to install all necessary dependencies.

### Development

To start local development server call `npm run start`. Now you can visit http://localhost:8080 to view this project.

### Testing

In order to run tests for this project call `npm run start` which will launch the development server and after the server is running, call `npm run test` to run test specs.

### Forking

This example is one of many projects in a common repository (monorepo). If you want to modify it, you have two options of forking it:

1. you can fork this entire repository
2. you can copy the example into a new repository. We have detailed instructions [here](../../../../README.md#copying-an-example-to-a-separate-repo) ("Copying an example to a separate repo").

## License

Handsontable is a commercial software with two licenses available:

- Free for non-commercial purposes such as teaching, academic research, and evaluation. [Read it here](https://github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf).
- Commercial license with support and maintenance included. See [pricing plans](https://handsontable.com/pricing).

## Technical support

If you have a commercial license and your support plan is active, contact our [Technical Support Team](https://handsontable.com/contact?category=technical_support).

We also support free-tier users through [GitHub issues](https://github.com/handsontable/handsontable/issues).
15 changes: 15 additions & 0 deletions examples/next/docs/js/hyperlink-formula/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>

<head>
<title>Handsontable for Javascript example</title>
<base href=".">
<meta charset="UTF-8" />
</head>

<body>
<div id="example"></div>
<script src="./src/index.js"></script>
</body>

</html>
31 changes: 31 additions & 0 deletions examples/next/docs/js/hyperlink-formula/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "hyperlink-formula",
"private": true,
"version": "0.0.0",
"description": "Javascript code snippet template",
"main": "index.html",
"homepage": "https://handsontable.com/",
"repository": {
"type": "git",
"url": "https://github.com/handsontable/handsontable.git"
},
"bugs": {
"url": "https://github.com/handsontable/handsontable/issues"
},
"author": "Handsoncode <[email protected]>",
"scripts": {
"start": "parcel index.html --port 8080",
"build": "parcel build index.html --public-url '.'",
"test": "node spec/support/jasmine.config.js"
},
"dependencies": {
"handsontable": "latest"
},
"devDependencies": {
"jasmine": "3.7.0",
"jasmine-console-reporter": "3.1.0",
"parcel-bundler": "1.12.3",
"puppeteer": "14.3.0"
},
"license": "SEE LICENSE IN LICENSE.txt"
}
25 changes: 25 additions & 0 deletions examples/next/docs/js/hyperlink-formula/spec/Smoke.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable */
const puppeteer = require('puppeteer');

describe('Smoke check', () => {
let browser = null;
let page = null;
const BASE_URL = process.env.TEST_URL || 'http://localhost:8080';

beforeEach(async () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
// browser = await puppeteer.launch({ headless: false, slowMo: 250, devtools: true });
browser = await puppeteer.launch();
page = await browser.newPage();
await page.goto(BASE_URL);
}, 90000);

afterEach(async () => {
await browser.close();
});

it('should fail', async () => {
// failing assertion, this should encourage developers to write tests for new code snippets
await expect(false).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// setup Jasmine
const Jasmine = require('jasmine');
const jasmine = new Jasmine();
jasmine.loadConfig({
spec_dir: 'spec',
spec_files: ['**/*[sS]pec.js'],
helpers: ['helpers/**/*.js'],
random: true,
stopSpecOnExpectationFailure: false
});
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000;

// setup terminal reporter
const JasmineReporter = require('jasmine-console-reporter');
const reporter = new JasmineReporter({
colors: 1,
cleanStack: 1,
verbosity: 4,
listStyle: 'indent',
timeUnit: 'ms',
timeThreshold: { ok: 500, warn: 1000, ouch: 3000 },
activity: false,
emoji: true,
beep: true
});

// initialize and execute
jasmine.env.clearReporters();
jasmine.addReporter(reporter);
jasmine.execute();
48 changes: 48 additions & 0 deletions examples/next/docs/js/hyperlink-formula/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import "handsontable/dist/handsontable.full.css";
import "./styles.css";

import Handsontable from "handsontable/dist/handsontable.full.min"
import HyperFormula from "hyperformula/dist/hyperformula";

const getDebugInfo = () => {
let debug = 'Handsontable:';
debug += ` v${Handsontable.version}`;
debug += ` (${Handsontable.buildDate})`;
debug += `\nHyperFormula: ${HyperFormula.version}`;
return debug;
}

const hyperformulaInstance = HyperFormula.buildEmpty({
licenseKey: 'internal-use-in-handsontable',
});

const data = [
['https://hyperformula.handsontable.com/', null],
['HyperFormula', null],
['=HYPERLINK("https://www.handsontable.com/")','=FORMULATEXT(A3)'],
['=HYPERLINK("http://www.handsontable.com","Handsontable")','=FORMULATEXT(A4)'],
['=HYPERLINK(A1,A2)','=FORMULATEXT(A5)'],
['=HYPERLINK(INDEX(A1:A2,1),INDEX(A1:A2,2))','=FORMULATEXT(A6)'],
['=CONCATENATE("Visit us at ", HYPERLINK(A1))','=FORMULATEXT(A7)'],
['=HYPERLINK()','=FORMULATEXT(A8)'],
['=HYPERLINK(123)','=FORMULATEXT(A9)'],
['=HYPERLINK(123,456)','=FORMULATEXT(A10)'],
['=HYPERLINK(123,456,789)','=FORMULATEXT(A11)'],
];

const container = document.getElementById('example');

const hot = new Handsontable(container, {
data,
formulas: {
engine: hyperformulaInstance,
sheetName: 'Sheet1'
},
width: '100%',
height: '100%',
rowHeaders: true,
colHeaders: true,
licenseKey: 'non-commercial-and-evaluation'
});

console.log(getDebugInfo());
19 changes: 19 additions & 0 deletions examples/next/docs/js/hyperlink-formula/src/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
html, body, #example {
box-sizing: border-box;
height: 100%;
width: 100%;
margin: 0;
}

html {
position: absolute;
top: 0;
left: 0;
padding: 0;
overflow: auto;
}

body {
padding: 1rem;
overflow: auto;
}
41 changes: 40 additions & 1 deletion handsontable/src/plugins/formulas/formulas.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { BasePlugin } from '../base';
import staticRegister from '../../utils/staticRegister';
import { error, warn } from '../../helpers/console';
import { isNumeric } from '../../helpers/number';
import { textRenderer } from '../../renderers/textRenderer';
import { hyperlinkRenderer } from '../../renderers/hyperlinkRenderer';
import {
isDefined,
isUndefined
Expand Down Expand Up @@ -819,7 +821,7 @@ export class Formulas extends BasePlugin {
if (isEscapedFormulaExpression(valueHolder.value)) {
valueHolder.value = unescapeFormulaExpression(valueHolder.value);
}

this.removeHyperLink(visualRow, column)
return;
}
}
Expand All @@ -829,6 +831,14 @@ export class Formulas extends BasePlugin {
col: this.columnAxisSyncer.getHfIndexFromVisualIndex(visualColumn),
sheet: this.sheetId
};

const hyperlink = this.engine.getCellHyperlink(address);
if (hyperlink !== undefined) {
this.addHyperLink(address.row, address.col, hyperlink)
} else {
this.removeHyperLink(address.row, address.col)
}

let cellValue = this.engine.getCellValue(address); // Date as an integer (Excel like date).
const cellMeta = this.hot.getCellMeta(visualRow, visualColumn);

Expand Down Expand Up @@ -1276,4 +1286,33 @@ export class Formulas extends BasePlugin {
onEngineSheetRemoved(removedSheetDisplayName, changes) {
this.hot.runHooks('afterSheetRemoved', removedSheetDisplayName, changes);
}

/**
* Called when a cell changes and it is now a HYPERLINK.
*
* @private
* @fires Hooks#beforeSetCellMeta
* @fires Hooks#afterSetCellMeta
* @param {number} row Visual row index.
* @param {number} column Visual column index.
* @param {string} url The url of the HYPERLINK cell.
*/
addHyperLink(row, col, url) {
this.hot.setCellMeta(row, col, 'formula.hyperlink', url)
this.hot.setCellMeta(row, col, 'renderer', hyperlinkRenderer)
}

/**
* Called when a cell changes and it is not a HYPERLINK.
*
* @private
* @fires Hooks#beforeSetCellMeta
* @fires Hooks#afterSetCellMeta
* @param {number} row Visual row index.
* @param {number} column Visual column index.
*/
removeHyperLink(row, col) {
this.hot.setCellMeta(row, col, 'formula.hyperlink')
this.hot.setCellMeta(row, col, 'renderer', textRenderer)
}
}
26 changes: 26 additions & 0 deletions handsontable/src/renderers/hyperlinkRenderer/hyperlinkRenderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { baseRenderer } from '../baseRenderer';
import { fastInnerHTML } from '../../helpers/dom/element';

export const RENDERER_TYPE = 'hyperlink';

/**
* @private
* @param {Core} instance The Handsontable instance.
* @param {HTMLTableCellElement} TD The rendered cell element.
* @param {number} row The visual row index.
* @param {number} col The visual column index.
* @param {number|string} prop The column property (passed when datasource is an array of objects).
* @param {*} value The rendered value.
* @param {object} cellProperties The cell meta object ({@see Core#getCellMeta}).
*/
export function hyperlinkRenderer(instance, TD, row, col, prop, value, cellProperties) {
baseRenderer.apply(this, [instance, TD, row, col, prop, value, cellProperties]);

const url = instance.getCellMeta(row, col)['formula.hyperlink']
if (url) {
fastInnerHTML(TD, value === null || value === void 0 ? '' : value, false);
TD.innerHTML = `<a href=\"${url}\">${value}</a>`
}
}

hyperlinkRenderer.RENDERER_TYPE = RENDERER_TYPE;
4 changes: 4 additions & 0 deletions handsontable/src/renderers/hyperlinkRenderer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export {
RENDERER_TYPE,
hyperlinkRenderer,
} from './hyperlinkRenderer';