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

Change signatures of simpleCellAddressToString and simpleCellRangeToString #1457

Merged
merged 8 commits into from
Nov 13, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- **Breaking change**: Change ES module build to use `mjs` files and `exports` property in `package.json` to make importing language files possible in Node environment. [#1344](https://github.com/handsontable/hyperformula/issues/1344)
- **Breaking change**: Removed the `binarySearchThreshold` configuration option. [#1439](https://github.com/handsontable/hyperformula/issues/1439)
- Make methods `simpleCellAddressToString` and `simpleCellRangeToString` more logical and easier to use. [#1151](https://github.com/handsontable/hyperformula/issues/1151)

## [2.7.1] - 2024-07-18

Expand Down
89 changes: 62 additions & 27 deletions src/HyperFormula.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {NamedExpression, NamedExpressionOptions, NamedExpressions} from './Named
import {normalizeAddedIndexes, normalizeRemovedIndexes} from './Operations'
import {
Ast,
AstNodeType, NamedExpressionDependency,
NamedExpressionDependency,
ParserWithCaching,
RelativeDependency,
simpleCellAddressFromString,
Expand Down Expand Up @@ -2791,10 +2791,10 @@ export class HyperFormula implements TypedEmitter {
/**
* Computes the simple (absolute) address of a cell address, based on its string representation.
* - If a sheet name is present in the string representation but is not present in the engine, returns `undefined`.
* - If no sheet name is present in the string representation, returns `contextSheetId` as sheet number.
* - If no sheet name is present in the string representation, uses `contextSheetId` as a sheet id in the returned address.
*
* @param {string} cellAddress - string representation of cell address in A1 notation
* @param {number} contextSheetId - context used in case of missing sheet in the first argument
* @param {number} contextSheetId - sheet id used to construct the simple address in case of missing sheet name in `cellAddress` argument
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
*
Expand All @@ -2803,17 +2803,17 @@ export class HyperFormula implements TypedEmitter {
* const hfInstance = HyperFormula.buildEmpty();
* hfInstance.addSheet('Sheet0'); //sheetId = 0
*
* // returns { sheet: 0, col: 0, row: 0 }
* const simpleCellAddress = hfInstance.simpleCellAddressFromString('A1', 0);
* // returns { sheet: 42, col: 0, row: 0 }
* const simpleCellAddress = hfInstance.simpleCellAddressFromString('A1', 42);
*
* // returns { sheet: 0, col: 0, row: 5 }
* const simpleCellAddress = hfInstance.simpleCellAddressFromString('Sheet1!A6');
* const simpleCellAddress = hfInstance.simpleCellAddressFromString('Sheet0!A6', 42);
*
* // returns { sheet: 0, col: 0, row: 5 }
* const simpleCellAddress = hfInstance.simpleCellAddressFromString('Sheet1!$A$6');
* const simpleCellAddress = hfInstance.simpleCellAddressFromString('Sheet0!$A$6', 42);
*
* // returns 'undefined', as there's no 'Sheet 2' in the HyperFormula instance
* const simpleCellAddress = hfInstance.simpleCellAddressFromString('Sheet2!A6');
* const simpleCellAddress = hfInstance.simpleCellAddressFromString('Sheet2!A6', 42);
* ```
*
* @category Helpers
Expand All @@ -2829,7 +2829,7 @@ export class HyperFormula implements TypedEmitter {
* If sheet name is present in string representation but not present in the engine, returns `undefined`.
*
* @param {string} cellRange - string representation of cell range in A1 notation
* @param {number} sheetId - context used in case of missing sheet in the first argument
* @param {number} contextSheetId - sheet id used to construct the simple address in case of missing sheet name in `cellRange` argument
*
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
Expand All @@ -2845,70 +2845,105 @@ export class HyperFormula implements TypedEmitter {
*
* @category Helpers
*/
public simpleCellRangeFromString(cellRange: string, sheetId: number): SimpleCellRange | undefined {
public simpleCellRangeFromString(cellRange: string, contextSheetId: number): SimpleCellRange | undefined {
validateArgToType(cellRange, 'string', 'cellRange')
validateArgToType(sheetId, 'number', 'sheetId')
return simpleCellRangeFromString(this.sheetMapping.get, cellRange, sheetId)
validateArgToType(contextSheetId, 'number', 'sheetId')
return simpleCellRangeFromString(this.sheetMapping.get, cellRange, contextSheetId)
}

/**
* Returns string representation of an absolute address in A1 notation or `undefined` if the sheet index is not present in the engine.
* Computes string representation of an absolute address in A1 notation. If `cellAddress.sheet` is not present in the engine, returns `undefined`.
*
* @param {SimpleCellAddress} cellAddress - object representation of an absolute address
* @param {number} sheetId - context used in case of missing sheet in the first argument
* @param {object | number} optionsOrContextSheetId - options object or number used as context sheet id to construct the string address (see examples)
*
* @throws [[ExpectedValueOfTypeError]] if its arguments are of wrong type
*
* @example
* ```js
* const hfInstance = HyperFormula.buildEmpty();
* hfInstance.addSheet('Sheet0'); //sheetId = 0
* const addr = { sheet: 0, col: 1, row: 1 };
*
* // should return 'B2'
* const A1Notation = hfInstance.simpleCellAddressToString({ sheet: 0, col: 1, row: 1 }, 0);
* const A1Notation = hfInstance.simpleCellAddressToString(addr);
*
* // should return 'B2'
* const A1Notation = hfInstance.simpleCellAddressToString(addr, { includeSheetName: false });
*
* // should return 'Sheet0!B2'
* const A1Notation = hfInstance.simpleCellAddressToString(addr, { includeSheetName: true });
*
* // should return 'B2' as context sheet id is the same as addr.sheet
* const A1Notation = hfInstance.simpleCellAddressToString(addr, 0);
*
* // should return 'Sheet0!B2' as context sheet id is different from addr.sheet
* const A1Notation = hfInstance.simpleCellAddressToString(addr, 42);
* ```
*
* @category Helpers
*/
public simpleCellAddressToString(cellAddress: SimpleCellAddress, sheetId: number): string | undefined {
public simpleCellAddressToString(cellAddress: SimpleCellAddress, optionsOrContextSheetId: { includeSheetName?: boolean } | number = {}) {
if (!isSimpleCellAddress(cellAddress)) {
throw new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')
}
validateArgToType(sheetId, 'number', 'sheetId')
return simpleCellAddressToString(this.sheetMapping.fetchDisplayName, cellAddress, sheetId)

const contextSheetId = typeof optionsOrContextSheetId === 'number'
? optionsOrContextSheetId
: optionsOrContextSheetId.includeSheetName ? cellAddress.sheet+1 : cellAddress.sheet

return simpleCellAddressToString(this.sheetMapping.fetchDisplayName, cellAddress, contextSheetId)
}

/**
* Returns string representation of an absolute range in A1 notation or `undefined` if the sheet index is not present in the engine.
* Computes string representation of an absolute range in A1 notation.
* Returns `undefined` if:
* - `cellRange` is not a valid range,
* - `cellRange.start.sheet` and `cellRange.start.end` are different,
* - `cellRange.start.sheet` is not present in the engine,
* - `cellRange.start.end` is not present in the engine.
*
* Note: This method is useful only for cell ranges; does not work with column ranges and row ranges.
*
* @param {SimpleCellRange} cellRange - object representation of an absolute range
* @param {number} sheetId - context used in case of missing sheet in the first argument
* @param {object | number} optionsOrContextSheetId - options object or number used as context sheet id to construct the string address (see examples)
*
* @throws [[ExpectedValueOfTypeError]] if its arguments are of wrong type
*
* @example
* ```js
* const hfInstance = HyperFormula.buildEmpty();
* hfInstance.addSheet('Sheet0'); //sheetId = 0
* hfInstance.addSheet('Sheet1'); //sheetId = 1
* const range = { start: { sheet: 0, col: 1, row: 1 }, end: { sheet: 0, col: 2, row: 1 } };
*
* // should return 'B2:C2'
* const A1Notation = hfInstance.simpleCellRangeToString(range);
*
* // should return 'B2:C2'
* const A1Notation = hfInstance.simpleCellRangeToString({ start: { sheet: 0, col: 1, row: 1 }, end: { sheet: 0, col: 2, row: 1 } }, 0);
* const A1Notation = hfInstance.simpleCellRangeToString(range, { includeSheetName: false });
*
* // should return 'Sheet0!B2:C2'
* const A1Notation = hfInstance.simpleCellRangeToString(range, { includeSheetName: true });
*
* // should return 'Sheet1!B2:C2'
* const another = hfInstance.simpleCellRangeToString({ start: { sheet: 1, col: 1, row: 1 }, end: { sheet: 1, col: 2, row: 1 } }, 0);
* // should return 'B2:C2' as context sheet id is the same as range.start.sheet and range.end.sheet
* const A1Notation = hfInstance.simpleCellRangeToString(range, 0);
*
* // should return 'Sheet0!B2:C2' as context sheet id is different from range.start.sheet and range.end.sheet
* const A1Notation = hfInstance.simpleCellRangeToString(range, 42);
* ```
*
* @category Helpers
*/
public simpleCellRangeToString(cellRange: SimpleCellRange, sheetId: number): string | undefined {
public simpleCellRangeToString(cellRange: SimpleCellRange, optionsOrContextSheetId: { includeSheetName?: boolean } | number = {}): string | undefined {
if (!isSimpleCellRange(cellRange)) {
throw new ExpectedValueOfTypeError('SimpleCellRange', 'cellRange')
}
validateArgToType(sheetId, 'number', 'sheetId')
return simpleCellRangeToString(this.sheetMapping.fetchDisplayName, cellRange, sheetId)

const contextSheetId = typeof optionsOrContextSheetId === 'number'
? optionsOrContextSheetId
: optionsOrContextSheetId.includeSheetName ? cellRange.start.sheet+cellRange.end.sheet+1 : cellRange.start.sheet

return simpleCellRangeToString(this.sheetMapping.fetchDisplayName, cellRange, contextSheetId)
}

/**
Expand Down
151 changes: 151 additions & 0 deletions test/engine.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,66 @@ describe('#simpleCellRangeToString', () => {
engine.simpleCellRangeToString({} as SimpleCellRange, 0)
}).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'cellRange'))
})

it('should work without second argument', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }}
const stringRange = hf.simpleCellRangeToString(cellRange)

expect(stringRange).toEqual('A1:B2')
})

it('should work with second argument `undefined`', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }}
const stringRange = hf.simpleCellRangeToString(cellRange, undefined)

expect(stringRange).toEqual('A1:B2')
})

it('should work with second argument `{}`', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }}
const stringRange = hf.simpleCellRangeToString(cellRange, {})

expect(stringRange).toEqual('A1:B2')
})

it('should work with second argument `{ includeSheetName: false }`', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }}
const stringRange = hf.simpleCellRangeToString(cellRange, { includeSheetName: false })

expect(stringRange).toEqual('A1:B2')
})

it('should work with second argument `{ includeSheetName: true }`', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }}
const stringRange = hf.simpleCellRangeToString(cellRange, { includeSheetName: true })

expect(stringRange).toEqual('Sheet0!A1:B2')
})
})

describe('#simpleCellAddressToString', () => {
Expand All @@ -1061,6 +1121,66 @@ describe('#simpleCellAddressToString', () => {
engine.simpleCellAddressToString({} as SimpleCellAddress, 0)
}).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress'))
})

it('should work without second argument', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellAddress = { sheet: 0, row: 0, col: 0 }
const stringAddress = hf.simpleCellAddressToString(cellAddress)

expect(stringAddress).toEqual('A1')
})

it('should work with second argument `undefined`', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellAddress = { sheet: 0, row: 0, col: 0 }
const stringAddress = hf.simpleCellAddressToString(cellAddress, undefined)

expect(stringAddress).toEqual('A1')
})

it('should work with second argument `{}`', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellAddress = { sheet: 0, row: 0, col: 0 }
const stringAddress = hf.simpleCellAddressToString(cellAddress, {})

expect(stringAddress).toEqual('A1')
})

it('should work with second argument `{ includeSheetName: false }`', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellAddress = { sheet: 0, row: 0, col: 0 }
const stringAddress = hf.simpleCellAddressToString(cellAddress, { includeSheetName: false })

expect(stringAddress).toEqual('A1')
})

it('should work with second argument `{ includeSheetName: true }`', () => {
const hf = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

const cellAddress = { sheet: 0, row: 0, col: 0 }
const stringAddress = hf.simpleCellAddressToString(cellAddress, { includeSheetName: true })

expect(stringAddress).toEqual('Sheet0!A1')
})
})

describe('#simpleCellAddressFromString', () => {
Expand Down Expand Up @@ -1098,4 +1218,35 @@ describe('#simpleCellRangeFromString', () => {
const engine = HyperFormula.buildEmpty()
expect(engine.simpleCellRangeFromString('A1:C1', 0)).toEqual(simpleCellRange(adr('A1'), adr('C1')))
})

it('should set sheetId to contestSheetId if there is no sheet name in the first argument', () => {
const engine = HyperFormula.buildEmpty()

expect(engine.simpleCellRangeFromString('A1:C1', 42)).toEqual(simpleCellRange(adr('A1', 42), adr('C1', 42)))
})

it('should set sheetId correctly if the sheet name is provided for the start of the range', () => {
const engine = HyperFormula.buildFromSheets({
Sheet0: []
})

expect(engine.simpleCellRangeFromString('Sheet0!A1:C1', 42)).toEqual(simpleCellRange(adr('A1', 0), adr('C1', 0)))
})

it('should set sheetId correctly if the same sheet name is provided for both ends of the range', () => {
const engine = HyperFormula.buildFromSheets({
Sheet0: []
})

expect(engine.simpleCellRangeFromString('Sheet0!A1:Sheet0!C1', 42)).toEqual(simpleCellRange(adr('A1', 0), adr('C1', 0)))
})

it('should return undefined if different sheet names are provided for the start and the end of the range', () => {
const engine = HyperFormula.buildFromSheets({
Sheet0: [],
Sheet1: []
})

expect(engine.simpleCellRangeFromString('Sheet0!A1:Sheet1!C1', 42)).toEqual(undefined)
})
})
Loading