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

Enforce stricter types for formatters in MappedMatrixVis #1703

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import visualizerStyles from '../../../visualizer/Visualizer.module.css';
import { useMappedArray, useSlicedDimsAndMapping } from '../hooks';
import type { MatrixVisConfig } from '../matrix/config';
import MatrixToolbar from '../matrix/MatrixToolbar';
import { getCellWidth, getFormatter } from '../matrix/utils';
import { getCellFormatter, getCellWidth } from '../matrix/utils';
import { getSliceSelection } from '../utils';

interface Props {
Expand Down Expand Up @@ -46,7 +46,7 @@ function MappedCompoundVis(props: Props) {
);

const fieldFormatters = Object.values(fields).map((field) =>
getFormatter(field, notation),
getCellFormatter(mappedArray, field, notation),
);

const { getExportURL } = useDataContext();
Expand All @@ -70,9 +70,7 @@ function MappedCompoundVis(props: Props) {
<MatrixVis
className={visualizerStyles.vis}
dims={mappedArray.shape}
cellFormatter={(row, col) =>
fieldFormatters[col](mappedArray.get(row, col))
}
cellFormatter={(row, col) => fieldFormatters[col](row, col)}
cellWidth={customCellWidth ?? cellWidth}
columnHeaders={fieldNames}
sticky={sticky}
Expand Down
8 changes: 3 additions & 5 deletions packages/app/src/vis-packs/core/matrix/MappedMatrixVis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useMappedArray, useSlicedDimsAndMapping } from '../hooks';
import { getSliceSelection } from '../utils';
import type { MatrixVisConfig } from './config';
import MatrixToolbar from './MatrixToolbar';
import { getCellWidth, getFormatter } from './utils';
import { getCellFormatter, getCellWidth } from './utils';

interface Props {
dataset: Dataset<ArrayShape, PrintableType>;
Expand All @@ -32,7 +32,7 @@ function MappedMatrixVis(props: Props) {
const [slicedDims, slicedMapping] = useSlicedDimsAndMapping(dims, dimMapping);
const mappedArray = useMappedArray(value, slicedDims, slicedMapping);

const formatter = getFormatter(type, notation);
const cellFormatter = getCellFormatter(mappedArray, type, notation);
const cellWidth = getCellWidth(type);

const { getExportURL } = useDataContext();
Expand All @@ -57,9 +57,7 @@ function MappedMatrixVis(props: Props) {
<MatrixVis
className={visualizerStyles.vis}
dims={mappedArray.shape}
cellFormatter={(row: number, col: number) =>
formatter(mappedArray.get(row, col))
}
cellFormatter={cellFormatter}
cellWidth={customCellWidth ?? cellWidth}
sticky={sticky}
/>
Expand Down
28 changes: 20 additions & 8 deletions packages/app/src/vis-packs/core/matrix/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Notation } from '@h5web/lib';
import {
assertNdArrayValue,
isBoolType,
isComplexType,
isEnumType,
isNumericType,
} from '@h5web/shared/guards';
import type {
BooleanType,
ArrayValue,
ComplexType,
NumericType,
PrintableCompoundType,
Expand All @@ -20,6 +21,7 @@ import {
formatBool,
} from '@h5web/shared/vis-utils';
import { format } from 'd3-format';
import type { NdArray } from 'ndarray';

export function createNumericFormatter(
notation: Notation,
Expand All @@ -45,27 +47,37 @@ export function createMatrixComplexFormatter(
return createComplexFormatter(formatStr, true);
}

export function getFormatter(
export function getCellFormatter(
dataArray: NdArray<ArrayValue<PrintableType>>,
type: PrintableType,
notation: Notation,
): ValueFormatter<PrintableType> {
): (row: number, col: number) => string {
if (isComplexType(type)) {
return createMatrixComplexFormatter(notation);
assertNdArrayValue(type, dataArray);
const formatter = createMatrixComplexFormatter(notation);
return (row, col) => formatter(dataArray.get(row, col));
}

if (isNumericType(type)) {
return createNumericFormatter(notation);
assertNdArrayValue(type, dataArray);
const formatter = createNumericFormatter(notation);
return (row, col) => formatter(dataArray.get(row, col));
}

if (isBoolType(type)) {
return formatBool as ValueFormatter<BooleanType>;
assertNdArrayValue(type, dataArray);
return (row, col) => formatBool(dataArray.get(row, col));
}

if (isEnumType(type)) {
return createEnumFormatter(type.mapping);
assertNdArrayValue(type, dataArray);
const formatter = createEnumFormatter(type.mapping);
return (row, col) => formatter(dataArray.get(row, col));
}

return (val) => (val as string).toString(); // call `toString()` for safety, in case type cast is wrong
// `StringType`
assertNdArrayValue(type, dataArray);
return (row, col) => dataArray.get(row, col);
}

export function getCellWidth(
Expand Down
10 changes: 10 additions & 0 deletions packages/shared/src/guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import type {
ArrayShape,
ArrayValue,
BooleanType,
ComplexArray,
ComplexType,
Expand Down Expand Up @@ -81,7 +82,7 @@

function assertNum(val: unknown): asserts val is number {
if (typeof val !== 'number') {
throw new TypeError('Expected number');

Check failure on line 85 in packages/shared/src/guards.ts

View workflow job for this annotation

GitHub Actions / test

src/__tests__/CorePack.test.tsx > visualize scalar compound dataset

Error: vitest-fail-on-console > Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using: vi.spyOn(console, 'error').mockImplementation(() => {}) and test that the warning occurs. Error: Uncaught [TypeError: Expected number] at reportException (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24) at innerInvokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:353:9) at invokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3) at HTMLUnknownElementImpl._dispatch (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9) at HTMLUnknownElementImpl.dispatchEvent (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17) at HTMLUnknownElement.dispatchEvent (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34) at Object.invokeGuardedCallbackDev (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:4213:16) at invokeGuardedCallback (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:4277:31) at beginWork$1 (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:27490:7) at performUnitOfWork (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26599:12) TypeError: Expected number at assertNum (/home/runner/work/h5web/h5web/packages/shared/src/guards.ts:85:11) at assertPrimitiveValue (/home/runner/work/h5web/h5web/packages/shared/src/guards.ts:430:5) at Module.assertNdArrayValue (/home/runner/work/h5web/h5web/packages/shared/src/guards.ts:447:5) at Module.getCellFormatter (/home/runner/work/h5web/h5web/packages/app/src/vis-packs/core/matrix/utils.ts:62:5) at map (/home/runner/work/h5web/h5web/packages/app/src/vis-packs/core/compound/MappedCompoundVis.tsx:49:5) at Array.map (<anonymous>) at MappedCompoundVis (/home/runner/work/h5web/h5web/packages/app/src/vis-packs/core/compound/MappedCompoundVis.tsx:48:49) at renderWithHooks (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:15486:18) at mountIndeterminateComponent (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:20103:13) at beginWork (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:21626:16) at Console.l [as error] (file:///home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]_@[email protected][email protected]_@[email protected][email protected]_/node_modules/vitest-fail-on-console/src/index.ts:90:31) at VirtualConsole.<anonymous> (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/virtual-console.js:29:45) at VirtualConsole.emit (node:events:519:28) at reportException (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:70:28) at innerInvokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:353:9) at invokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/E

Check failure on line 85 in packages/shared/src/guards.ts

View workflow job for this annotation

GitHub Actions / test

src/__tests__/CorePack.test.tsx > visualize 1D compound dataset

Error: vitest-fail-on-console > Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using: vi.spyOn(console, 'error').mockImplementation(() => {}) and test that the warning occurs. Error: Uncaught [TypeError: Expected number] at reportException (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24) at innerInvokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:353:9) at invokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3) at HTMLUnknownElementImpl._dispatch (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9) at HTMLUnknownElementImpl.dispatchEvent (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17) at HTMLUnknownElement.dispatchEvent (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34) at Object.invokeGuardedCallbackDev (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:4213:16) at invokeGuardedCallback (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:4277:31) at beginWork$1 (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:27490:7) at performUnitOfWork (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26599:12) TypeError: Expected number at assertNum (/home/runner/work/h5web/h5web/packages/shared/src/guards.ts:85:11) at assertPrimitiveValue (/home/runner/work/h5web/h5web/packages/shared/src/guards.ts:430:5) at Module.assertNdArrayValue (/home/runner/work/h5web/h5web/packages/shared/src/guards.ts:447:5) at Module.getCellFormatter (/home/runner/work/h5web/h5web/packages/app/src/vis-packs/core/matrix/utils.ts:62:5) at map (/home/runner/work/h5web/h5web/packages/app/src/vis-packs/core/compound/MappedCompoundVis.tsx:49:5) at Array.map (<anonymous>) at MappedCompoundVis (/home/runner/work/h5web/h5web/packages/app/src/vis-packs/core/compound/MappedCompoundVis.tsx:48:49) at renderWithHooks (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:15486:18) at mountIndeterminateComponent (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:20103:13) at beginWork (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:21626:16) at Console.l [as error] (file:///home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]_@[email protected][email protected]_@[email protected][email protected]_/node_modules/vitest-fail-on-console/src/index.ts:90:31) at VirtualConsole.<anonymous> (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/virtual-console.js:29:45) at VirtualConsole.emit (node:events:519:28) at reportException (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:70:28) at innerInvokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:353:9) at invokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/E

Check failure on line 85 in packages/shared/src/guards.ts

View workflow job for this annotation

GitHub Actions / test

src/__tests__/VisSelector.test.tsx > choose most advanced visualization when switching between datasets

Error: vitest-fail-on-console > Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using: vi.spyOn(console, 'error').mockImplementation(() => {}) and test that the warning occurs. Error: Uncaught [TypeError: Expected number] at reportException (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24) at innerInvokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:353:9) at invokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3) at HTMLUnknownElementImpl._dispatch (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9) at HTMLUnknownElementImpl.dispatchEvent (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17) at HTMLUnknownElement.dispatchEvent (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34) at Object.invokeGuardedCallbackDev (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:4213:16) at invokeGuardedCallback (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:4277:31) at beginWork$1 (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:27490:7) at performUnitOfWork (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26599:12) TypeError: Expected number at assertNum (/home/runner/work/h5web/h5web/packages/shared/src/guards.ts:85:11) at assertPrimitiveValue (/home/runner/work/h5web/h5web/packages/shared/src/guards.ts:430:5) at Module.assertNdArrayValue (/home/runner/work/h5web/h5web/packages/shared/src/guards.ts:447:5) at Module.getCellFormatter (/home/runner/work/h5web/h5web/packages/app/src/vis-packs/core/matrix/utils.ts:62:5) at map (/home/runner/work/h5web/h5web/packages/app/src/vis-packs/core/compound/MappedCompoundVis.tsx:49:5) at Array.map (<anonymous>) at MappedCompoundVis (/home/runner/work/h5web/h5web/packages/app/src/vis-packs/core/compound/MappedCompoundVis.tsx:48:49) at renderWithHooks (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:15486:18) at mountIndeterminateComponent (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:20103:13) at beginWork (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:21626:16) at Console.l [as error] (file:///home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected][email protected]_@[email protected][email protected]_@[email protected][email protected]_/node_modules/vitest-fail-on-console/src/index.ts:90:31) at VirtualConsole.<anonymous> (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/virtual-console.js:29:45) at VirtualConsole.emit (node:events:519:28) at reportException (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:70:28) at innerInvokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:353:9) at invokeEventListeners (/home/runner/work/h5web/h5web/node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/living/events/E
}
}

Expand Down Expand Up @@ -438,6 +439,15 @@
}
}

export function assertNdArrayValue<T extends DType>(
type: T,
value: NdArray<unknown[] | TypedArray>,
): asserts value is NdArray<ArrayValue<T>> {
if (value.data.length > 0) {
assertPrimitiveValue(type, value.data[0]);
}
}

export function assertDatasetValue<D extends Dataset<ScalarShape | ArrayShape>>(
value: unknown,
dataset: D,
Expand Down
Loading