diff --git a/vuu-ui/packages/vuu-table/src/table-next/TableNext.tsx b/vuu-ui/packages/vuu-table/src/table-next/TableNext.tsx
index f585ad55c..3f2726f22 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/TableNext.tsx
+++ b/vuu-ui/packages/vuu-table/src/table-next/TableNext.tsx
@@ -29,7 +29,6 @@ export const TableNext = forwardRef(function TableNext(
navigationStyle = "cell",
onAvailableColumnsChange,
onConfigChange,
- onFeatureEnabled,
onFeatureInvocation,
onRowClick: onRowClickProp,
onSelect,
@@ -73,7 +72,6 @@ export const TableNext = forwardRef(function TableNext(
navigationStyle,
onAvailableColumnsChange,
onConfigChange,
- onFeatureEnabled,
onFeatureInvocation,
onRowClick: onRowClickProp,
onSelect,
diff --git a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/cell-utils.ts b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/cell-utils.ts
new file mode 100644
index 000000000..fee46d456
--- /dev/null
+++ b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/cell-utils.ts
@@ -0,0 +1,12 @@
+import { TableCellRendererProps } from "packages/vuu-datagrid-types";
+/**
+ * A memo compare function for cell renderers. Can be used to suppress
+ * render where column and data are both unchanged. Avoids render
+ * when row changes, where changes in row are unrelated to this cell.
+ */
+export const dataAndColumnUnchanged = (
+ p: TableCellRendererProps,
+ p1: TableCellRendererProps
+) =>
+ p.column === p1.column &&
+ p.row[p.columnMap[p.column.name]] === p1.row[p1.columnMap[p1.column.name]];
diff --git a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/dropdown-cell/DropdownCell.tsx b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/dropdown-cell/DropdownCell.tsx
index 88cf3efb7..52c3b975f 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/dropdown-cell/DropdownCell.tsx
+++ b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/dropdown-cell/DropdownCell.tsx
@@ -1,52 +1,60 @@
+import { useLookupValues } from "@finos/vuu-data-react";
+import { ListOption, TableCellRendererProps } from "@finos/vuu-datagrid-types";
import {
+ dispatchCommitEvent,
Dropdown,
DropdownOpenKey,
SingleSelectionHandler,
+ WarnCommit,
} from "@finos/vuu-ui-controls";
-import {
- isColumnTypeRenderer,
- isTypeDescriptor,
- registerComponent,
-} from "@finos/vuu-utils";
-import { TableCellProps } from "@finos/vuu-datagrid-types";
-// import { dispatchCommitEvent } from "@finos/vuu-ui-controls";
+import { registerComponent } from "@finos/vuu-utils";
+import { VuuColumnDataType } from "@finos/vuu-protocol-types";
+import { memo, useCallback, useState } from "react";
+import { dataAndColumnUnchanged } from "../cell-utils";
import "./DropdownCell.css";
-import { useCallback, useState } from "react";
const classBase = "vuuTableDropdownCell";
const openKeys: DropdownOpenKey[] = ["Enter", " "];
-export const DropdownCell = ({ column, columnMap, row }: TableCellProps) => {
- const values =
- isTypeDescriptor(column.type) && isColumnTypeRenderer(column.type?.renderer)
- ? column.type?.renderer?.values
- : [];
-
+export const DropdownCell = memo(function DropdownCell({
+ column,
+ columnMap,
+ onCommit = WarnCommit,
+ row,
+}: TableCellRendererProps) {
const dataIdx = columnMap[column.name];
- const [value, setValue] = useState(row[dataIdx]);
- const handleSelectionChange = useCallback
(
- (evt, selectedItem) => {
- if (selectedItem) {
- setValue(selectedItem);
- // dispatchCommitEvent(evt.target as HTMLElement);
+ const { initialValue, values } = useLookupValues(column, row[dataIdx]);
+
+ const [value, setValue] = useState(null);
+
+ const handleSelectionChange = useCallback>(
+ (evt, selectedOption) => {
+ if (selectedOption) {
+ setValue(selectedOption);
+ onCommit(selectedOption.value as VuuColumnDataType).then((response) => {
+ if (response === true && evt) {
+ dispatchCommitEvent(evt.target as HTMLElement);
+ }
+ });
}
},
- []
+ [onCommit]
);
return (
-
className={classBase}
onSelectionChange={handleSelectionChange}
openKeys={openKeys}
- selected={value}
+ selected={value ?? initialValue}
source={values}
width={column.width - 17} // temp hack
/>
);
-};
+},
+dataAndColumnUnchanged);
registerComponent("dropdown-cell", DropdownCell, "cell-renderer", {});
diff --git a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/index.ts b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/index.ts
index b602eec50..593451edf 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/index.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/index.ts
@@ -1,2 +1,3 @@
export * from "./dropdown-cell";
export * from "./input-cell";
+export * from "./toggle-cell";
diff --git a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/input-cell/InputCell.tsx b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/input-cell/InputCell.tsx
index cd0b8a890..b1add91f7 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/input-cell/InputCell.tsx
+++ b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/input-cell/InputCell.tsx
@@ -11,11 +11,11 @@ import "./InputCell.css";
const classBase = "vuuTableInputCell";
-const WarnCommit = () => {
+const WarnCommit = (): Promise => {
console.warn(
"onCommit handler has not been provided to InputCell cell renderer"
);
- return true;
+ return Promise.resolve(true);
};
export const InputCell = ({
column,
diff --git a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/toggle-cell/ToggleCell.css b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/toggle-cell/ToggleCell.css
new file mode 100644
index 000000000..cffcda938
--- /dev/null
+++ b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/toggle-cell/ToggleCell.css
@@ -0,0 +1,32 @@
+
+.vuuTableToggleCell {
+ --saltButton-borderRadius: 4px;
+ --saltButton-height: 16px;
+ font-weight: 500;
+ position: relative;
+ top: 1px;;
+}
+.vuuTableToggleCell-side {
+ --saltButton-minWidth: 40px;
+}
+.vuuTableToggleCell.vuuCycleStateButton-buy {
+ background-color: var(--vuu-color-green-50);
+}
+
+.vuuTableToggleCell.vuuCycleStateButton-sell {
+ background-color: var(--vuu-color-red-50);
+
+}
+
+.vuuTableNextCell .vuuTableToggleCell:focus {
+ /* TODO fix use of important */
+ border: solid 2px var(--vuu-color-purple-10) !important;
+ color: white !important;
+ height: 18px !important;
+ top: 0px !important;
+}
+
+
+
+
+
diff --git a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/toggle-cell/ToggleCell.tsx b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/toggle-cell/ToggleCell.tsx
new file mode 100644
index 000000000..4c5bbe9d1
--- /dev/null
+++ b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/toggle-cell/ToggleCell.tsx
@@ -0,0 +1,71 @@
+import {
+ ColumnDescriptor,
+ TableCellRendererProps,
+} from "@finos/vuu-datagrid-types";
+import {
+ CycleStateButtonProps,
+ dispatchCommitEvent,
+ WarnCommit,
+} from "@finos/vuu-ui-controls";
+import {
+ isTypeDescriptor,
+ isValueListRenderer,
+ registerComponent,
+} from "@finos/vuu-utils";
+import cx from "classnames";
+
+import { memo, useCallback } from "react";
+import { dataAndColumnUnchanged } from "../cell-utils";
+import { CycleStateButton } from "@finos/vuu-ui-controls";
+
+import "./ToggleCell.css";
+
+const classBase = "vuuTableToggleCell";
+
+const getValueList = ({ name, type }: ColumnDescriptor) => {
+ if (isTypeDescriptor(type) && isValueListRenderer(type.renderer)) {
+ return type.renderer.values;
+ } else {
+ throw Error(
+ `useLookupValues column ${name} has not been configured with a values list`
+ );
+ }
+};
+
+export const ToggleCell = memo(function ToggleCell({
+ column,
+ columnMap,
+ onCommit = WarnCommit,
+ row,
+}: TableCellRendererProps) {
+ const values = getValueList(column);
+ const dataIdx = columnMap[column.name];
+ const value = row[dataIdx];
+
+ const handleCommit = useCallback(
+ (evt, value) => {
+ return onCommit(value).then((response) => {
+ if (response === true) {
+ dispatchCommitEvent(evt.target as HTMLElement);
+ }
+ return response;
+ });
+ },
+ [onCommit]
+ );
+
+ return (
+
+ {value}
+
+ );
+},
+dataAndColumnUnchanged);
+
+registerComponent("toggle-cell", ToggleCell, "cell-renderer", {});
diff --git a/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/toggle-cell/index.ts b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/toggle-cell/index.ts
new file mode 100644
index 000000000..3d7185458
--- /dev/null
+++ b/vuu-ui/packages/vuu-table/src/table-next/cell-renderers/toggle-cell/index.ts
@@ -0,0 +1 @@
+export * from "./ToggleCell";
diff --git a/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts b/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts
index 8c6db9865..9c7d25131 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts
@@ -1,2 +1,2 @@
export * from "./buildContextMenuDescriptors";
-export * from "./useTableContextMenu";
+export * from "./useHandleTableContextMenu";
diff --git a/vuu-ui/packages/vuu-table/src/table-next/context-menu/useTableContextMenu.ts b/vuu-ui/packages/vuu-table/src/table-next/context-menu/useHandleTableContextMenu.ts
similarity index 99%
rename from vuu-ui/packages/vuu-table/src/table-next/context-menu/useTableContextMenu.ts
rename to vuu-ui/packages/vuu-table/src/table-next/context-menu/useHandleTableContextMenu.ts
index fc3813a85..70b84b051 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/context-menu/useTableContextMenu.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/context-menu/useHandleTableContextMenu.ts
@@ -49,7 +49,7 @@ const removeFilterColumn = (
const { Average, Count, Distinct, High, Low, Sum } = AggregationType;
-export const useTableContextMenu = ({
+export const useHandleTableContextMenu = ({
dataSource,
onPersistentColumnOperation,
}: ContextMenuHookProps) => {
diff --git a/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css b/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css
index 8522a0901..e9a9a58cb 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css
+++ b/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css
@@ -34,6 +34,7 @@
line-height: calc(var(--header-height) - 1px);
overflow: hidden;
text-overflow: ellipsis;
+ white-space: nowrap;
}
.vuuTableNextHeaderCell-right .vuuTableNextHeaderCell-label {
diff --git a/vuu-ui/packages/vuu-table/src/table-next/table-cell/TableCell.tsx b/vuu-ui/packages/vuu-table/src/table-next/table-cell/TableCell.tsx
index 91dbea8b6..c6c3ecf61 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/table-cell/TableCell.tsx
+++ b/vuu-ui/packages/vuu-table/src/table-next/table-cell/TableCell.tsx
@@ -1,12 +1,17 @@
-import { TableCellProps } from "@finos/vuu-datagrid-types";
-import { metadataKeys } from "@finos/vuu-utils";
-import { VuuColumnDataType } from "@finos/vuu-protocol-types";
+import {
+ DataItemCommitHandler,
+ TableCellProps,
+} from "@finos/vuu-datagrid-types";
+import {
+ VuuColumnDataType,
+ VuuRowDataItemType,
+} from "@finos/vuu-protocol-types";
+import { isNumericColumn } from "@finos/vuu-utils";
import { MouseEventHandler, useCallback } from "react";
import { useCell } from "../useCell";
import "./TableCell.css";
-const { IDX } = metadataKeys;
const classBase = "vuuTableNextCell";
export const TableCell = ({
@@ -20,13 +25,24 @@ export const TableCell = ({
const { CellRenderer, name, valueFormatter } = column;
const dataIdx = columnMap[name];
- const handleDataItemEdited = useCallback(
- (value: VuuColumnDataType) => {
- onDataEdited?.(row, name, value);
- // TODO will only return false in case of server rejection
- return true;
+ const handleDataItemEdited = useCallback(
+ (value) => {
+ if (onDataEdited) {
+ let typedValue = value;
+ if (isNumericColumn(column) && typeof value === "string") {
+ typedValue =
+ column.serverDataType === "double"
+ ? parseFloat(value)
+ : parseInt(value);
+ }
+ return onDataEdited?.(row, name, typedValue);
+ } else {
+ throw Error(
+ "TableCell onDataEdited prop not supplied for an editable cell"
+ );
+ }
},
- [name, onDataEdited, row]
+ [column, name, onDataEdited, row]
);
const handleClick = useCallback(
diff --git a/vuu-ui/packages/vuu-table/src/table-next/table-dom-utils.ts b/vuu-ui/packages/vuu-table/src/table-next/table-dom-utils.ts
index 8976179b8..b0006e1a3 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/table-dom-utils.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/table-dom-utils.ts
@@ -33,5 +33,5 @@ export const getTableCell = (
export const cellIsEditable = (cell: HTMLDivElement) =>
cell.classList.contains("vuuTableNextCell-editable");
-export const cellIsTextInput = (cell: HTMLDivElement) =>
+export const cellIsTextInput = (cell: HTMLElement) =>
cell.querySelector(".vuuTableInputCell") !== null;
diff --git a/vuu-ui/packages/vuu-table/src/table-next/useCellEditing.ts b/vuu-ui/packages/vuu-table/src/table-next/useCellEditing.ts
index f50a7d320..bb24595a3 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/useCellEditing.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/useCellEditing.ts
@@ -1,5 +1,9 @@
import { isCharacterKey } from "@finos/vuu-utils";
-import { KeyboardEvent as ReactKeyboardEvent, useCallback } from "react";
+import {
+ FocusEventHandler,
+ KeyboardEvent as ReactKeyboardEvent,
+ useCallback,
+} from "react";
import { cellIsTextInput } from "./table-dom-utils";
export interface CellEditingHookProps {
@@ -11,39 +15,28 @@ export const useCellEditing = ({ navigate }: CellEditingHookProps) => {
navigate();
}, [navigate]);
- const editInput = useCallback(
- (evt: ReactKeyboardEvent) => {
- const cellEl = evt.target as HTMLDivElement;
- const input = cellEl.querySelector("input");
- if (input) {
- input.focus();
- input.select();
- }
- // TODO dergister on blur
- // TODO need a custom event
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore commit is a custom event fired by vuu inputs
- cellEl.addEventListener("vuu-commit", commitHandler, true);
- },
- [commitHandler]
- );
+ const editInput = useCallback((evt: ReactKeyboardEvent) => {
+ const cellEl = evt.target as HTMLDivElement;
+ const input = cellEl.querySelector("input");
+ if (input) {
+ input.focus();
+ input.select();
+ }
+ }, []);
- const focusInput = useCallback(
- (evt: ReactKeyboardEvent) => {
- const cellEl = evt.target as HTMLDivElement;
- const input = cellEl.querySelector("input");
- if (input) {
- input.focus();
- input.select();
- }
- cellEl.addEventListener("vuu-commit", commitHandler, true);
- },
- [commitHandler]
- );
+ const focusInput = useCallback((evt: ReactKeyboardEvent) => {
+ const cellEl = evt.target as HTMLDivElement;
+ const input = cellEl.querySelector("input");
+ if (input) {
+ input.focus();
+ input.select();
+ }
+ }, []);
const handleKeyDown = useCallback(
(e: ReactKeyboardEvent) => {
- if (cellIsTextInput(e.target as HTMLDivElement)) {
+ const el = e.target as HTMLElement;
+ if (cellIsTextInput(el)) {
if (isCharacterKey(e.key)) {
editInput(e);
} else if (e.key === "Enter") {
@@ -54,7 +47,25 @@ export const useCellEditing = ({ navigate }: CellEditingHookProps) => {
[editInput, focusInput]
);
+ const handleBlur = useCallback(
+ (e) => {
+ const el = e.target as HTMLElement;
+ el.removeEventListener("vuu-commit", commitHandler, true);
+ },
+ [commitHandler]
+ );
+
+ const handleFocus = useCallback(
+ (e) => {
+ const el = e.target as HTMLElement;
+ el.addEventListener("vuu-commit", commitHandler, true);
+ },
+ [commitHandler]
+ );
+
return {
+ onBlur: handleBlur,
+ onFocus: handleFocus,
onKeyDown: handleKeyDown,
};
};
diff --git a/vuu-ui/packages/vuu-table/src/table-next/useDataSource.ts b/vuu-ui/packages/vuu-table/src/table-next/useDataSource.ts
index cb901d82a..7a02d8af8 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/useDataSource.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/useDataSource.ts
@@ -1,14 +1,10 @@
import {
DataSource,
DataSourceSubscribedMessage,
+ isVuuFeatureInvocation,
SubscribeCallback,
VuuFeatureInvocationMessage,
- VuuFeatureMessage,
} from "@finos/vuu-data";
-import {
- isVuuFeatureAction,
- isVuuFeatureInvocation,
-} from "@finos/vuu-data-react/src";
import { DataSourceRow } from "@finos/vuu-data-types";
import { VuuRange } from "@finos/vuu-protocol-types";
import { getFullRange, NULL_RANGE } from "@finos/vuu-utils";
@@ -18,7 +14,6 @@ import { MovingWindow } from "./moving-window";
export interface DataSourceHookProps {
dataSource: DataSource;
// onConfigChange?: (message: DataSourceConfigMessage) => void;
- onFeatureEnabled?: (message: VuuFeatureMessage) => void;
onFeatureInvocation?: (message: VuuFeatureInvocationMessage) => void;
onSizeChange: (size: number) => void;
onSubscribed: (subscription: DataSourceSubscribedMessage) => void;
@@ -28,7 +23,6 @@ export interface DataSourceHookProps {
export const useDataSource = ({
dataSource,
- onFeatureEnabled,
onFeatureInvocation,
onSizeChange,
onSubscribed,
@@ -77,22 +71,13 @@ export const useDataSource = ({
data.current = dataWindow.data;
hasUpdated.current = true;
}
- } else if (isVuuFeatureAction(message)) {
- onFeatureEnabled?.(message);
} else if (isVuuFeatureInvocation(message)) {
onFeatureInvocation?.(message);
} else {
console.log(`useDataSource unexpected message ${message.type}`);
}
},
- [
- dataWindow,
- onFeatureEnabled,
- onFeatureInvocation,
- onSizeChange,
- onSubscribed,
- setData,
- ]
+ [dataWindow, onFeatureInvocation, onSizeChange, onSubscribed, setData]
);
const getSelectedRows = useCallback(() => {
@@ -127,11 +112,15 @@ export const useDataSource = ({
// }, [refreshIfUpdated]);
useEffect(() => {
- //TODO could we improve this by using a ref for range ?
- dataSource?.subscribe(
- { range: getFullRange(range, renderBufferSize) },
- datasourceMessageHandler
- );
+ if (dataSource.status === "disabled") {
+ dataSource.enable?.(datasourceMessageHandler);
+ } else {
+ //TODO could we improve this by using a ref for range ?
+ dataSource?.subscribe(
+ { range: getFullRange(range, renderBufferSize) },
+ datasourceMessageHandler
+ );
+ }
}, [dataSource, datasourceMessageHandler, range, renderBufferSize]);
const setRange = useCallback(
diff --git a/vuu-ui/packages/vuu-table/src/table-next/useKeyboardNavigation.ts b/vuu-ui/packages/vuu-table/src/table-next/useKeyboardNavigation.ts
index e1b4ddf24..426c3c73a 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/useKeyboardNavigation.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/useKeyboardNavigation.ts
@@ -60,19 +60,17 @@ const howFarIsCellOutsideViewport = (
cellEl: HTMLElement
): readonly [ScrollDirection | undefined, number | undefined] => {
//TODO lots of scope for optimisation here
- const scrollbarContainer = cellEl
- .closest(".vuuTableNext")
- ?.querySelector(".vuuTableNext-scrollbarContainer");
- if (scrollbarContainer) {
- const viewport = scrollbarContainer?.getBoundingClientRect();
+ const contentContainer = cellEl.closest(".vuuTableNext-contentContainer");
+ if (contentContainer) {
+ const viewport = contentContainer?.getBoundingClientRect();
const cell = cellEl.closest(".vuuTableNextCell")?.getBoundingClientRect();
if (cell) {
if (cell.bottom > viewport.bottom) {
return ["down", cell.bottom - viewport.bottom];
} else if (cell.top < viewport.top) {
return ["up", cell.top - viewport.top];
- } else if (cell.right < viewport.right) {
- return ["right", cell.right - viewport.right];
+ } else if (cell.right > viewport.right) {
+ return ["right", cell.right + 6 - viewport.right];
} else if (cell.left < viewport.left) {
return ["left", cell.left - viewport.left];
} else {
@@ -107,7 +105,8 @@ function nextCellPos(
return [rowIdx + 1, colIdx];
}
} else if (key === "ArrowRight") {
- if (colIdx < columnCount - 1) {
+ // The colIdx is 1 based, because of the selection decorator
+ if (colIdx < columnCount) {
return [rowIdx, colIdx + 1];
} else {
return [rowIdx, colIdx];
@@ -186,7 +185,7 @@ NavigationHookProps) => {
if (direction && distance) {
requestScroll?.({ type: "scroll-distance", distance, direction });
}
- activeCell.focus();
+ activeCell.focus({ preventScroll: true });
}
}
},
@@ -259,7 +258,6 @@ NavigationHookProps) => {
const [nextRowIdx, nextColIdx] = isPagingKey(key)
? await nextPageItemIdx(key, activeCellPos.current)
: nextCellPos(key, activeCellPos.current, columnCount, rowCount);
- console.log(`nextRowIdx ${nextRowIdx} nextColIdx ${nextColIdx}`);
const [rowIdx, colIdx] = activeCellPos.current;
if (nextRowIdx !== rowIdx || nextColIdx !== colIdx) {
diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts
index 339539b61..945e98938 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts
@@ -45,7 +45,7 @@ export const useTableContextMenu = ({
});
}
},
- [columns, data, dataSource, showContextMenu]
+ [columns, data, dataSource, getSelectedRows, showContextMenu]
);
return onContextMenu;
diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts
index 4b963d86b..d44b9dc4f 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts
@@ -18,10 +18,9 @@ import {
isFilteredColumn,
isGroupColumn,
isPinned,
- isTypeDescriptor,
logger,
metadataKeys,
- moveItem,
+ replaceColumn,
sortPinnedColumns,
stripFilterFromColumns,
subscribedOnly,
@@ -41,12 +40,6 @@ const KEY_OFFSET = metadataKeys.count;
const columnWithoutDataType = ({ serverDataType }: ColumnDescriptor) =>
serverDataType === undefined;
-const getCellRendererForColumn = (column: ColumnDescriptor) => {
- if (isTypeDescriptor(column.type)) {
- return getCellRenderer(column.type?.renderer);
- }
-};
-
const getDataType = (
column: ColumnDescriptor,
tableSchema: TableSchema
@@ -107,7 +100,6 @@ export interface ColumnActionMove {
type: "moveColumn";
column: KeyedColumnDescriptor;
moveBy?: 1 | -1;
- moveTo?: number;
}
export interface ColumnActionPin {
@@ -307,7 +299,7 @@ const columnDescriptorToKeyedColumDescriptor =
const keyedColumnWithDefaults = {
...rest,
align,
- CellRenderer: getCellRendererForColumn(column),
+ CellRenderer: getCellRenderer(column),
clientSideEditValidationCheck: hasValidationRules(column.type)
? buildValidationChecker(column.type.renderer.rules)
: undefined,
@@ -331,7 +323,8 @@ const columnDescriptorToKeyedColumDescriptor =
function moveColumn(
state: InternalTableModel,
- { column, moveBy, moveTo }: ColumnActionMove
+ // TODO do we ever use this ?
+ { column, moveBy }: ColumnActionMove
) {
const { columns } = state;
if (typeof moveBy === "number") {
@@ -343,12 +336,6 @@ function moveColumn(
...state,
columns: newColumns,
};
- } else if (typeof moveTo === "number") {
- const index = columns.indexOf(column);
- return {
- ...state,
- columns: moveItem(columns, index, moveTo),
- };
}
return state;
}
@@ -513,10 +500,3 @@ function updateTableConfig(
return result;
}
-
-function replaceColumn(
- state: KeyedColumnDescriptor[],
- column: KeyedColumnDescriptor
-) {
- return state.map((col) => (col.name === column.name ? column : col));
-}
diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts
index aa8266cc1..4c37b7d4a 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts
@@ -24,10 +24,12 @@ import {
isJsonGroup,
isValidNumber,
metadataKeys,
+ moveColumnTo,
updateColumn,
visibleColumnAtIndex,
} from "@finos/vuu-utils";
import {
+ FocusEvent,
KeyboardEvent,
MouseEvent,
RefObject,
@@ -38,17 +40,19 @@ import {
} from "react";
import {
buildContextMenuDescriptors,
+ ColumnActionHide,
+ ColumnActionPin,
MeasuredProps,
RowClickHandler,
TableProps,
useSelection,
- useTableContextMenu,
} from "../table";
import { TableColumnResizeHandler } from "./column-resizing";
import { updateTableConfig } from "./table-config";
import { useDataSource } from "./useDataSource";
import { useInitialValue } from "./useInitialValue";
-import { useTableContextMenu as useTableContextMenuNext } from "./useTableContextMenu";
+import { useTableContextMenu } from "./useTableContextMenu";
+import { useHandleTableContextMenu } from "./context-menu";
import { useCellEditing } from "./useCellEditing";
import {
isShowColumnSettings,
@@ -70,7 +74,6 @@ export interface TableHookProps
| "navigationStyle"
| "onAvailableColumnsChange"
| "onConfigChange"
- | "onFeatureEnabled"
| "onFeatureInvocation"
| "onSelect"
| "onSelectionChange"
@@ -102,7 +105,6 @@ export const useTable = ({
navigationStyle = "cell",
onAvailableColumnsChange,
onConfigChange,
- onFeatureEnabled,
onFeatureInvocation,
onRowClick: onRowClickProp,
onSelect,
@@ -146,6 +148,21 @@ export const useTable = ({
});
}, [tableConfig, dataSource.config, dispatchColumnAction]);
+ const applyTableConfigChange = useCallback(
+ (config: TableConfig) => {
+ dispatchColumnAction({
+ type: "init",
+ tableConfig: config,
+ dataSourceConfig: dataSource.config,
+ });
+ onConfigChange?.(config);
+ },
+ [dataSource.config, dispatchColumnAction, onConfigChange]
+ );
+
+ /**
+ * These stateColumns are required only for the duration of a column resize operation
+ */
const [stateColumns, setStateColumns] = useState();
const [columns, setColumnSize] = useMemo(() => {
const setSize = (columnName: string, width: number) => {
@@ -182,30 +199,33 @@ export const useTable = ({
const onSubscribed = useCallback(
({ tableSchema }: DataSourceSubscribedMessage) => {
if (tableSchema) {
- // dispatchColumnAction({
- // type: "setTableSchema",
- // tableSchema,
- // });
+ dispatchColumnAction({
+ type: "setTableSchema",
+ tableSchema,
+ });
} else {
console.log("subscription message with no schema");
}
},
- []
+ [dispatchColumnAction]
);
- const { data, getSelectedRows, onEditTableData, range, setRange } =
- useDataSource({
- dataSource,
- onFeatureEnabled,
- onFeatureInvocation,
- renderBufferSize,
- onSizeChange: onDataRowcountChange,
- onSubscribed,
- range: initialRange,
- });
+ const { data, getSelectedRows, range, setRange } = useDataSource({
+ dataSource,
+ onFeatureInvocation,
+ renderBufferSize,
+ onSizeChange: onDataRowcountChange,
+ onSubscribed,
+ range: initialRange,
+ });
const handleConfigChanged = useCallback(
(tableConfig: TableConfig) => {
+ // console.log(
+ // `useTableNext handleConfigChanged`,
+ // JSON.stringify(tableConfig, null, 2)
+ // );
+
dispatchColumnAction({
type: "init",
tableConfig,
@@ -226,23 +246,6 @@ export const useTable = ({
[dataSource]
);
- const handleCreateCalculatedColumn = useCallback(
- (column: ColumnDescriptor) => {
- dataSource.columns = dataSource.columns.concat(column.name);
- const newTableConfig = addColumn(tableConfig, column);
- dispatchColumnAction({
- type: "init",
- tableConfig: newTableConfig,
- dataSourceConfig: dataSource.config,
- });
- console.log(`dispatch onConfigChange`, {
- newTableConfig,
- });
- onConfigChange?.(newTableConfig);
- },
- [dataSource, dispatchColumnAction, onConfigChange, tableConfig]
- );
-
useEffect(() => {
dataSource.on("config", (config, confirmed) => {
dispatchColumnAction({
@@ -253,6 +256,42 @@ export const useTable = ({
});
}, [dataSource, dispatchColumnAction]);
+ const handleCreateCalculatedColumn = useCallback(
+ (column: ColumnDescriptor) => {
+ dataSource.columns = dataSource.columns.concat(column.name);
+ applyTableConfigChange(addColumn(tableConfig, column));
+ },
+ [dataSource, tableConfig, applyTableConfigChange]
+ );
+
+ const hideColumns = useCallback(
+ (action: ColumnActionHide) => {
+ const { columns } = action;
+ const hiddenColumns = columns.map((c) => c.name);
+ const newTableConfig = {
+ ...tableConfig,
+ columns: tableConfig.columns.map((col) =>
+ hiddenColumns.includes(col.name) ? { ...col, hidden: true } : col
+ ),
+ };
+ applyTableConfigChange(newTableConfig);
+ },
+ [tableConfig, applyTableConfigChange]
+ );
+
+ const pinColumn = useCallback(
+ (action: ColumnActionPin) => {
+ applyTableConfigChange({
+ ...tableConfig,
+ columns: updateColumn(tableConfig.columns, {
+ ...action.column,
+ pin: action.pin,
+ }),
+ });
+ },
+ [tableConfig, applyTableConfigChange]
+ );
+
const { showColumnSettingsPanel, showTableSettingsPanel } =
useTableAndColumnSettings({
availableColumns:
@@ -275,14 +314,26 @@ export const useTable = ({
} else if (isShowTableSettings(action)) {
showTableSettingsPanel();
} else {
- // expectConfigChangeRef.current = true;
- dispatchColumnAction(action);
+ switch (action.type) {
+ case "hideColumns":
+ return hideColumns(action);
+ case "pinColumn":
+ return pinColumn(action);
+ default:
+ dispatchColumnAction(action);
+ }
}
},
- [dispatchColumnAction, showColumnSettingsPanel, showTableSettingsPanel]
+ [
+ dispatchColumnAction,
+ hideColumns,
+ pinColumn,
+ showColumnSettingsPanel,
+ showTableSettingsPanel,
+ ]
);
- const handleContextMenuAction = useTableContextMenu({
+ const handleContextMenuAction = useHandleTableContextMenu({
dataSource,
onPersistentColumnOperation,
});
@@ -321,6 +372,7 @@ export const useTable = ({
column,
width,
});
+ setStateColumns(undefined);
onConfigChange?.(
updateTableConfig(tableConfig, {
type: "col-size",
@@ -409,6 +461,7 @@ export const useTable = ({
const {
navigate,
+ onFocus: navigationFocus,
onKeyDown: navigationKeyDown,
...containerProps
} = useKeyboardNavigation({
@@ -421,7 +474,23 @@ export const useTable = ({
viewportRowCount: viewportMeasurements.rowCount,
});
- const { onKeyDown: editingKeyDown } = useCellEditing({ navigate });
+ const {
+ onBlur: editingBlur,
+ onKeyDown: editingKeyDown,
+ onFocus: editingFocus,
+ } = useCellEditing({
+ navigate,
+ });
+
+ const handleFocus = useCallback(
+ (e: FocusEvent) => {
+ navigationFocus();
+ if (!e.defaultPrevented) {
+ editingFocus(e);
+ }
+ },
+ [editingFocus, navigationFocus]
+ );
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
@@ -433,7 +502,7 @@ export const useTable = ({
[navigationKeyDown, editingKeyDown]
);
- const onContextMenu = useTableContextMenuNext({
+ const onContextMenu = useTableContextMenu({
columns,
data,
dataSource,
@@ -504,22 +573,26 @@ export const useTable = ({
const handleDrop = useCallback(
(moveFrom: number, moveTo: number) => {
- // onMoveColumn?.(fromIndex, toIndex);
- const column = columns[moveFrom];
+ const column = tableConfig.columns[moveFrom];
+
+ const newTableConfig = {
+ ...tableConfig,
+ columns: moveColumnTo(tableConfig.columns, column, moveTo),
+ };
dispatchColumnAction({
- type: "moveColumn",
- column,
- moveTo,
+ type: "init",
+ tableConfig: newTableConfig,
+ dataSourceConfig: dataSource.config,
});
+ onConfigChange?.(newTableConfig);
},
- [columns, dispatchColumnAction]
+ [dataSource.config, dispatchColumnAction, onConfigChange, tableConfig]
);
const handleDataEdited = useCallback(
- (rowIndex, columnName, value) => {
- return dataSource.applyEdit(rowIndex, columnName, value);
- },
+ async (row, columnName, value) =>
+ dataSource.applyEdit(row, columnName, value),
[dataSource]
);
@@ -543,6 +616,8 @@ export const useTable = ({
return {
...containerProps,
+ onBlur: editingBlur,
+ onFocus: handleFocus,
onKeyDown: handleKeyDown,
columnMap,
columns,
diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableScroll.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableScroll.ts
index 00a6a8853..ef47a7469 100644
--- a/vuu-ui/packages/vuu-table/src/table-next/useTableScroll.ts
+++ b/vuu-ui/packages/vuu-table/src/table-next/useTableScroll.ts
@@ -201,7 +201,7 @@ export const useTableScroll = ({
scrollbarContainer.scrollTo({
top: newScrollTop,
left: newScrollLeft,
- behavior: "auto",
+ behavior: "smooth",
});
} else if (scrollRequest.type === "scroll-page") {
const { direction } = scrollRequest;
diff --git a/vuu-ui/packages/vuu-table/src/table/Table.tsx b/vuu-ui/packages/vuu-table/src/table/Table.tsx
index 49be16bda..2b996ae13 100644
--- a/vuu-ui/packages/vuu-table/src/table/Table.tsx
+++ b/vuu-ui/packages/vuu-table/src/table/Table.tsx
@@ -30,7 +30,6 @@ export const Table = ({
height,
id: idProp,
onConfigChange,
- onFeatureEnabled,
onFeatureInvocation,
onSelect,
onSelectionChange,
@@ -60,7 +59,6 @@ export const Table = ({
headerHeight,
height,
onConfigChange,
- onFeatureEnabled,
onFeatureInvocation,
onSelectionChange,
rowHeight,
diff --git a/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts b/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts
index db4ec0320..fc3813a85 100644
--- a/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts
+++ b/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts
@@ -21,6 +21,11 @@ export interface ContextMenuOptions {
}
export interface ContextMenuHookProps {
dataSource?: DataSource;
+ /**
+ * A persistent Column Operation is any manipulation of a table column that should be
+ * persisted across user sessions. e.g. if user pins a column, column should still be
+ * pinned next time user opens app.
+ */
onPersistentColumnOperation: (action: PersistentColumnAction) => void;
}
diff --git a/vuu-ui/packages/vuu-table/src/table/dataTableTypes.ts b/vuu-ui/packages/vuu-table/src/table/dataTableTypes.ts
index ec8c2be16..4faded45f 100644
--- a/vuu-ui/packages/vuu-table/src/table/dataTableTypes.ts
+++ b/vuu-ui/packages/vuu-table/src/table/dataTableTypes.ts
@@ -13,7 +13,7 @@ import {
TableSelectionModel,
} from "@finos/vuu-datagrid-types";
import { VuuDataRow } from "@finos/vuu-protocol-types";
-import { MeasuredContainerProps } from "packages/vuu-layout/src";
+import { MeasuredContainerProps } from "@finos/vuu-layout";
import { FC, MouseEvent } from "react";
import { RowProps } from "../table-next/Row";
@@ -50,12 +50,6 @@ export interface TableProps extends Omit {
* prop, table state can be persisted across sessions.
*/
onConfigChange?: (config: TableConfig) => void;
- /**
- * Features like context menu actions and visual links are enabled by the Vuu server.
- * This callback allows us to receive a notification when such a feature is available.
- * The options provided must then be used to configure appropriate UI affordances.
- */
- onFeatureEnabled?: (message: VuuFeatureMessage) => void;
/**
* When a Vuu feature e.g. context menu action, has been invoked, the Vuu server
* response must be handled. This callback provides that response.
diff --git a/vuu-ui/packages/vuu-table/src/table/useDataSource.ts b/vuu-ui/packages/vuu-table/src/table/useDataSource.ts
index d018100e3..8599987bf 100644
--- a/vuu-ui/packages/vuu-table/src/table/useDataSource.ts
+++ b/vuu-ui/packages/vuu-table/src/table/useDataSource.ts
@@ -2,22 +2,19 @@ import {
DataSource,
DataSourceConfigMessage,
DataSourceSubscribedMessage,
+ isVuuFeatureAction,
+ isVuuFeatureInvocation,
SubscribeCallback,
VuuFeatureInvocationMessage,
VuuFeatureMessage,
} from "@finos/vuu-data";
import { DataSourceRow } from "@finos/vuu-data-types";
-import {
- isVuuFeatureAction,
- isVuuFeatureInvocation,
-} from "@finos/vuu-data-react";
import { VuuRange, VuuSortCol } from "@finos/vuu-protocol-types";
import {
getFullRange,
isRowSelectedLast,
metadataKeys,
- RowSelected,
WindowRange,
} from "@finos/vuu-utils";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
diff --git a/vuu-ui/packages/vuu-table/src/table/useTableModel.ts b/vuu-ui/packages/vuu-table/src/table/useTableModel.ts
index a5ad0c328..e2193e310 100644
--- a/vuu-ui/packages/vuu-table/src/table/useTableModel.ts
+++ b/vuu-ui/packages/vuu-table/src/table/useTableModel.ts
@@ -30,8 +30,7 @@ import {
import { Reducer, useReducer } from "react";
import { VuuColumnDataType } from "@finos/vuu-protocol-types";
-import { DataSourceConfig } from "@finos/vuu-data";
-import { TableSchema } from "@finos/vuu-data/src/message-utils";
+import { DataSourceConfig, TableSchema } from "@finos/vuu-data";
const DEFAULT_COLUMN_WIDTH = 100;
const KEY_OFFSET = metadataKeys.count;
@@ -41,7 +40,7 @@ const columnWithoutDataType = ({ serverDataType }: ColumnDescriptor) =>
const getCellRendererForColumn = (column: ColumnDescriptor) => {
if (isTypeDescriptor(column.type)) {
- return getCellRenderer(column.type?.renderer);
+ return getCellRenderer(column);
}
};
diff --git a/vuu-ui/packages/vuu-theme/css/components/button.css b/vuu-ui/packages/vuu-theme/css/components/button.css
index d201a23da..d35d0a9ab 100644
--- a/vuu-ui/packages/vuu-theme/css/components/button.css
+++ b/vuu-ui/packages/vuu-theme/css/components/button.css
@@ -1,11 +1,11 @@
.saltButton {
white-space: nowrap;;
+ --saltButton-borderRadius: 6px;
}
.saltButton-primary {
--saltButton-borderColor: var(--salt-actionable-primary-foreground);
--saltButton-borderWidth: 1px;
- --saltButton-borderRadius: 6px;
--saltButton-borderStyle: solid;
--vuu-icon-color: var(--saltIcon-color);
}
diff --git a/vuu-ui/packages/vuu-theme/css/components/input.css b/vuu-ui/packages/vuu-theme/css/components/input.css
index 58d2edd44..3e89014aa 100644
--- a/vuu-ui/packages/vuu-theme/css/components/input.css
+++ b/vuu-ui/packages/vuu-theme/css/components/input.css
@@ -2,6 +2,10 @@
display: none;
}
+.saltInput:hover {
+ background: inherit;;
+}
+
.saltInput-primary {
--saltInput-height: 24px;
border: solid 1px var(--input-borderColor, var(--salt-editable-borderColor));
diff --git a/vuu-ui/packages/vuu-theme/css/components/toggle-button.css b/vuu-ui/packages/vuu-theme/css/components/toggle-button.css
index 3bae4e002..fbfdbad5a 100644
--- a/vuu-ui/packages/vuu-theme/css/components/toggle-button.css
+++ b/vuu-ui/packages/vuu-theme/css/components/toggle-button.css
@@ -5,7 +5,7 @@
}
.saltToggleButtonGroup-horizontal .saltToggleButton {
- height: 24px;
+ height: var(--vuuToggleButton-height, 24px);
}
.vuuIconToggleButton {
diff --git a/vuu-ui/packages/vuu-theme/css/foundations/color.css b/vuu-ui/packages/vuu-theme/css/foundations/color.css
index ea52e17e8..e058e225f 100644
--- a/vuu-ui/packages/vuu-theme/css/foundations/color.css
+++ b/vuu-ui/packages/vuu-theme/css/foundations/color.css
@@ -5,12 +5,15 @@
--vuu-color-transparent: transparent;
--vuu-color-black: black;
--vuu-color-white: white;
+ --vuu-color-white-fade-70: rgba(255,255,255,.7);
/** text-selection */
--vuu-color-blue-40: rgb(164, 213, 244); /* #A4D5F4 */
--vuu-color-purple-10: rgb(109,24,189); /* #6D18BD */
+ --vuu-color-purple-20-fade-40 :rgba(197, 163, 229, .4); /* #C5A3E566 */
+ --vuu-color-purple-50: rgb(42, 1, 95); /* #2A015F */
--vuu-color-purple-10-fade-light: rgba(109,24,189, var(--vuu-fade-light));
--vuu-color-pink-10: rgb(234, 120, 128); /* #F37880 */
diff --git a/vuu-ui/packages/vuu-theme/css/palette/interact.css b/vuu-ui/packages/vuu-theme/css/palette/interact.css
index 1c1b5fd6c..456a9c140 100644
--- a/vuu-ui/packages/vuu-theme/css/palette/interact.css
+++ b/vuu-ui/packages/vuu-theme/css/palette/interact.css
@@ -21,12 +21,12 @@
--salt-palette-interact-cta-background: var(--vuu-color-purple-10);
--salt-palette-interact-cta-background-active: var(--vuu-color-purple-10);
--salt-palette-interact-cta-background-activeDisabled: var(--salt-color-blue-700-fade-background);
- --salt-palette-interact-cta-background-disabled: var(--salt-color-blue-600-fade-background);
+ --salt-palette-interact-cta-background-disabled: var(--vuu-color-purple-20-fade-40);
--salt-palette-interact-cta-background-hover: var(--vuu-color-pink-10);
--salt-palette-interact-cta-foreground: var(--salt-color-white);
--salt-palette-interact-cta-foreground-active: var(--salt-color-white);
--salt-palette-interact-cta-foreground-activeDisabled: var(--salt-color-white-fade-foreground);
- --salt-palette-interact-cta-foreground-disabled: var(--salt-color-white-fade-foreground);
+ --salt-palette-interact-cta-foreground-disabled: var(--vuu-color-white-fade-70);
--salt-palette-interact-cta-foreground-hover: var(--vuu-color-gray-80);
--salt-palette-interact-primary-background: var(--vuu-color-white);
--salt-palette-interact-primary-background-active: var(--vuu-color-gray-50);
@@ -73,7 +73,7 @@
--salt-palette-interact-cta-background: var(--salt-color-blue-600);
--salt-palette-interact-cta-background-active: var(--salt-color-blue-700);
--salt-palette-interact-cta-background-activeDisabled: var(--salt-color-blue-700-fade-background);
- --salt-palette-interact-cta-background-disabled: var(--salt-color-blue-600-fade-background);
+ --salt-palette-interact-cta-background-disabled: var(--vuu-color-purple-20-fade-40);
--salt-palette-interact-cta-background-hover: var(--salt-color-blue-500);
--salt-palette-interact-cta-foreground: var(--salt-color-white);
--salt-palette-interact-cta-foreground-active: var(--salt-color-white);
diff --git a/vuu-ui/packages/vuu-ui-controls/src/common-hooks/selectionTypes.ts b/vuu-ui/packages/vuu-ui-controls/src/common-hooks/selectionTypes.ts
index 59bfae0df..3196773bc 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/common-hooks/selectionTypes.ts
+++ b/vuu-ui/packages/vuu-ui-controls/src/common-hooks/selectionTypes.ts
@@ -90,10 +90,17 @@ export interface SelectionHookResult {
setSelected: (selected: string[]) => void;
}
+/**
+ * evt is only null in the special case of a selection fired from a multi-select
+ * host on tab or selection based on freeform text in combobox
+ */
export type MultiSelectionHandler- = (
event: SyntheticEvent | null,
selected: Item[]
) => void;
+/**
+ * evt is only null in the special case of freeform text in combobox
+ */
export type SingleSelectionHandler
- = (
event: SyntheticEvent | null,
selected: Item
diff --git a/vuu-ui/packages/vuu-ui-controls/src/cycle-state-button/CycleStateButton.css b/vuu-ui/packages/vuu-ui-controls/src/cycle-state-button/CycleStateButton.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/vuu-ui/packages/vuu-ui-controls/src/cycle-state-button/CycleStateButton.tsx b/vuu-ui/packages/vuu-ui-controls/src/cycle-state-button/CycleStateButton.tsx
new file mode 100644
index 000000000..7615b6306
--- /dev/null
+++ b/vuu-ui/packages/vuu-ui-controls/src/cycle-state-button/CycleStateButton.tsx
@@ -0,0 +1,57 @@
+import { Button, ButtonProps } from "@salt-ds/core";
+import cx from "classnames";
+import { CommitResponse } from "packages/vuu-datagrid-types";
+import {
+ VuuColumnDataType,
+ VuuRowDataItemType,
+} from "packages/vuu-protocol-types";
+import { ForwardedRef, forwardRef, SyntheticEvent, useCallback } from "react";
+
+const classBase = "vuuCycleStateButton";
+
+export interface CycleStateButtonProps extends ButtonProps {
+ onCommit: (evt: SyntheticEvent, value: VuuRowDataItemType) => CommitResponse;
+ values: string[];
+ value: string;
+}
+
+const getNextValue = (value: string, valueList: string[]) => {
+ const index = valueList.indexOf(value);
+ if (index === valueList.length - 1) {
+ return valueList[0];
+ } else {
+ return valueList[index + 1];
+ }
+};
+
+export const CycleStateButton = forwardRef(function CycleStateButton(
+ { className, onCommit, value, values, ...buttonProps }: CycleStateButtonProps,
+ forwardedRef: ForwardedRef
+) {
+ const handleClick = useCallback(
+ (evt: SyntheticEvent) => {
+ const nextValue = getNextValue(value, values);
+ onCommit(evt, nextValue as VuuColumnDataType).then((response) => {
+ if (response !== true) {
+ console.error(response);
+ }
+ });
+ },
+ [onCommit, value, values]
+ );
+
+ return (
+
+ );
+});
diff --git a/vuu-ui/packages/vuu-ui-controls/src/cycle-state-button/index.ts b/vuu-ui/packages/vuu-ui-controls/src/cycle-state-button/index.ts
new file mode 100644
index 000000000..6fd693266
--- /dev/null
+++ b/vuu-ui/packages/vuu-ui-controls/src/cycle-state-button/index.ts
@@ -0,0 +1 @@
+export * from "./CycleStateButton";
diff --git a/vuu-ui/packages/vuu-ui-controls/src/dropdown/Dropdown.css b/vuu-ui/packages/vuu-ui-controls/src/dropdown/Dropdown.css
index c5187606c..4782939ba 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/dropdown/Dropdown.css
+++ b/vuu-ui/packages/vuu-ui-controls/src/dropdown/Dropdown.css
@@ -1,10 +1,5 @@
.vuuDropdown {
--saltIcon-margin: 2px 0 0 8px;
- --saltButton-borderStyle: solid;
- --saltButton-borderColor: var(--salt-editable-borderColor);
- --saltButton-borderWidth: 1px;
- --saltButton-borderRadius: 6px;
- --saltButton-height: var(--vuuDropdown-height, auto);
display: inline-block;
line-height: 0;
@@ -12,6 +7,14 @@
width: var(--vuuDropdown-width, auto);
}
+.vuuDropdownButton.saltButton-secondary {
+ --saltButton-borderStyle: solid;
+ --saltButton-borderColor: var(--salt-editable-borderColor);
+ --saltButton-borderWidth: 1px;
+ --saltButton-borderRadius: 6px;
+ --saltButton-height: var(--vuuDropdown-height, auto);
+}
+
.vuuDropdown-fullWidth {
width: 100%;
}
diff --git a/vuu-ui/packages/vuu-ui-controls/src/dropdown/dropdownTypes.ts b/vuu-ui/packages/vuu-ui-controls/src/dropdown/dropdownTypes.ts
index ad862944c..5a5096d05 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/dropdown/dropdownTypes.ts
+++ b/vuu-ui/packages/vuu-ui-controls/src/dropdown/dropdownTypes.ts
@@ -14,6 +14,7 @@ export type CloseReason =
| "Escape"
| "click-away"
| "select"
+ | "script"
| "Tab"
| "toggle";
export type OpenChangeHandler = (
diff --git a/vuu-ui/packages/vuu-ui-controls/src/dropdown/useDropdown.ts b/vuu-ui/packages/vuu-ui-controls/src/dropdown/useDropdown.ts
index e350b861a..8a2e6a2b2 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/dropdown/useDropdown.ts
+++ b/vuu-ui/packages/vuu-ui-controls/src/dropdown/useDropdown.ts
@@ -53,21 +53,18 @@ export const useDropdown =
- ({
const handleSelectionChange = useCallback(
(evt, selected) => {
- console.log(`useDropdown onSelectionChange`, {
- selected,
- });
if (!isMultiSelect) {
setIsOpen(false);
onOpenChange?.(false);
}
if (Array.isArray(selected)) {
(onSelectionChange as MultiSelectionHandler
- )?.(
- null,
+ evt,
selected as Item[]
);
} else if (selected) {
(onSelectionChange as SingleSelectionHandler
- )?.(
- null,
+ evt,
selected as Item
);
}
diff --git a/vuu-ui/packages/vuu-ui-controls/src/editable/useEditableText.ts b/vuu-ui/packages/vuu-ui-controls/src/editable/useEditableText.ts
index f9cc2aaf3..4e1038d6e 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/editable/useEditableText.ts
+++ b/vuu-ui/packages/vuu-ui-controls/src/editable/useEditableText.ts
@@ -1,4 +1,5 @@
-import { VuuColumnDataType } from "@finos/vuu-protocol-types";
+import { VuuRowDataItemType } from "@finos/vuu-protocol-types";
+import { DataItemCommitHandler } from "packages/vuu-datagrid-types";
import {
FormEventHandler,
KeyboardEvent,
@@ -8,12 +9,19 @@ import {
} from "react";
import { ClientSideValidationChecker } from "./editable-utils";
+export const WarnCommit = (): Promise => {
+ console.warn(
+ "onCommit handler has not been provided to InputCell cell renderer"
+ );
+ return Promise.resolve(true);
+};
+
export interface EditableTextHookProps<
- T extends VuuColumnDataType = VuuColumnDataType
+ T extends VuuRowDataItemType = VuuRowDataItemType
> {
clientSideEditValidationCheck?: ClientSideValidationChecker;
initialValue: T;
- onCommit: (value: T) => boolean;
+ onCommit: DataItemCommitHandler;
}
export const dispatchCommitEvent = (el: HTMLElement) => {
@@ -22,7 +30,7 @@ export const dispatchCommitEvent = (el: HTMLElement) => {
};
export const useEditableText = <
- T extends VuuColumnDataType = VuuColumnDataType
+ T extends VuuRowDataItemType = VuuRowDataItemType
>({
clientSideEditValidationCheck,
initialValue,
@@ -34,10 +42,6 @@ export const useEditableText = <
const isDirtyRef = useRef(false);
const hasCommittedRef = useRef(false);
- const handleBlur = useCallback(() => {
- console.log("blur");
- }, []);
-
const handleKeyDown = useCallback(
(evt: KeyboardEvent) => {
if (evt.key === "Enter") {
@@ -49,11 +53,14 @@ export const useEditableText = <
setMessage(warningMessage);
} else {
setMessage(undefined);
- // if we want to potentially await server ACK here, need async
- if (onCommit(value)) {
- isDirtyRef.current = false;
- dispatchCommitEvent(evt.target as HTMLInputElement);
- }
+ onCommit(value).then((response) => {
+ if (response === true) {
+ isDirtyRef.current = false;
+ dispatchCommitEvent(evt.target as HTMLInputElement);
+ } else {
+ setMessage(response);
+ }
+ });
}
} else {
dispatchCommitEvent(evt.target as HTMLInputElement);
@@ -82,10 +89,8 @@ export const useEditableText = <
const { value } = evt.target as HTMLInputElement;
isDirtyRef.current = value !== initialValueRef.current;
setValue(value as T);
- console.log(`value changes to ${value} message ${message}`);
if (hasCommittedRef.current) {
const warningMessage = clientSideEditValidationCheck?.(value);
- console.log({ warningMessage });
if (warningMessage !== message && warningMessage !== false) {
setMessage(warningMessage);
}
@@ -95,7 +100,6 @@ export const useEditableText = <
);
return {
- onBlur: handleBlur,
onChange: handleChange,
onKeyDown: handleKeyDown,
value,
diff --git a/vuu-ui/packages/vuu-ui-controls/src/expando-input/ExpandoInput.css b/vuu-ui/packages/vuu-ui-controls/src/expando-input/ExpandoInput.css
index 794f43fde..5c10aab65 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/expando-input/ExpandoInput.css
+++ b/vuu-ui/packages/vuu-ui-controls/src/expando-input/ExpandoInput.css
@@ -28,7 +28,10 @@
visibility: hidden;
white-space: pre-wrap;
}
-
+ .vuuExpandoInput-error {
+ border-color: var(--vuu-color-red-50);
+ }
+
.vuuExpandoInput .saltInput {
font-weight: var(--salt-text-fontWeight);
left: var(--expandoInput-padding, 0);
diff --git a/vuu-ui/packages/vuu-ui-controls/src/expando-input/ExpandoInput.tsx b/vuu-ui/packages/vuu-ui-controls/src/expando-input/ExpandoInput.tsx
index 9ab9341d8..5b8bfd0e3 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/expando-input/ExpandoInput.tsx
+++ b/vuu-ui/packages/vuu-ui-controls/src/expando-input/ExpandoInput.tsx
@@ -1,4 +1,4 @@
-import { Input, InputProps } from "@salt-ds/core";
+import { VuuInput, VuuInputProps } from "@finos/vuu-ui-controls";
import cx from "classnames";
import { ForwardedRef, forwardRef } from "react";
@@ -6,15 +6,35 @@ import "./ExpandoInput.css";
const classBase = "vuuExpandoInput";
+const noop = () => undefined;
+
+export interface ExpandoInputProps extends Omit {
+ onCommit?: VuuInputProps["onCommit"];
+}
+
export const ExpandoInput = forwardRef(function ExpandoInput(
- { className: classNameProp, value, inputProps, ...InputProps }: InputProps,
+ {
+ className: classNameProp,
+ errorMessage,
+ value,
+ inputProps,
+ onCommit = noop,
+ ...InputProps
+ }: ExpandoInputProps,
forwardedRef: ForwardedRef
) {
return (
-
-
+
{
setIsOpen(open);
onOpenChange?.(open, closeReason);
- if (open === false) {
- dataSource.unsubscribe();
- }
+ // if (open === false) {
+ // dataSource.unsubscribe();
+ // }
},
- [dataSource, onOpenChange, setIsOpen]
+ [onOpenChange, setIsOpen]
);
const handleInputChange = useCallback(
@@ -92,6 +92,9 @@ export const useInstrumentPicker = ({
);
const inputProps = {
+ inputProps: {
+ autoComplete: "off",
+ },
onChange: handleInputChange,
};
const controlProps = {};
diff --git a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/Tabstrip.css b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/Tabstrip.css
index e0a111663..5178a0301 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/Tabstrip.css
+++ b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/Tabstrip.css
@@ -18,6 +18,10 @@
/* Tabstrip orientation is horizontal */
.vuuTabstrip-horizontal {
+ --vuuOverflowContainer-borderColor: var(--salt-container-primary-borderColor);
+ --vuuOverflowContainer-borderStyle: none none solid none;
+ --vuuOverflowContainer-borderBottomWidth: 1px;
+
--tabstrip-height: var(--vuuTabstrip-height, 28px);
--tabstrip-width: var(--vuuTabstrip-width, 100%);
--tab-height: var(--tabstrip-height);
@@ -27,7 +31,7 @@
--tab-thumb-top: auto;
--tab-thumb-width: var(--tab-thumb-size, 100%);
align-items: flex-start;
- border-bottom: var(--vuuTabstrip-borderBottom, solid 1px var(--salt-container-primary-borderColor));
+ /* border-bottom: var(--vuuTabstrip-borderBottom, solid 1px var(--salt-container-primary-borderColor)); */
}
/* Tabstrip orientation is vertical */
@@ -107,7 +111,7 @@
}
.vuuDraggable-tabstrip-horizontal {
- --item-height: var(--tabstrip-height);
+ --overflow-item-height: var(--tabstrip-height);
--tab-thumb-height: 2px;
--tab-thumb-left: 0px;
--tabstrip-display: inline-flex;
diff --git a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/Tabstrip.tsx b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/Tabstrip.tsx
index bf7ee27c1..1ebb1a1f8 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/Tabstrip.tsx
+++ b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/Tabstrip.tsx
@@ -136,7 +136,7 @@ export const Tabstrip = ({
{...htmlAttributes}
{...tabstripHook.containerProps}
className={className}
- height={28}
+ height={29}
id={id}
orientation={orientation}
overflowIcon="more-horiz"
diff --git a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts
index beefb4049..03161598e 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts
+++ b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts
@@ -40,7 +40,6 @@ export const useAnimatedSelectionThumb = (
isValidNumber(newPosition) &&
isValidNumber(oldSize)
) {
- console.log({ orientation, positionProp, oldPosition, newPosition });
offset = oldPosition - newPosition;
size = oldSize;
const speed = orientation === "horizontal" ? 1100 : 700;
diff --git a/vuu-ui/packages/vuu-ui-controls/src/vuu-input/VuuInput.css b/vuu-ui/packages/vuu-ui-controls/src/vuu-input/VuuInput.css
index e69de29bb..af090a2a2 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/vuu-input/VuuInput.css
+++ b/vuu-ui/packages/vuu-ui-controls/src/vuu-input/VuuInput.css
@@ -0,0 +1,7 @@
+.vuuInput {
+ --vuu-icon-size: 16px;
+}
+
+.vuuInput-errorIcon:after {
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/vuu-ui/packages/vuu-ui-controls/src/vuu-input/VuuInput.tsx b/vuu-ui/packages/vuu-ui-controls/src/vuu-input/VuuInput.tsx
index fdf95e3d3..81cd31a31 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/vuu-input/VuuInput.tsx
+++ b/vuu-ui/packages/vuu-ui-controls/src/vuu-input/VuuInput.tsx
@@ -4,13 +4,23 @@ import { Input, InputProps } from "@salt-ds/core";
import cx from "classnames";
import {
FocusEventHandler,
+ ForwardedRef,
+ forwardRef,
KeyboardEventHandler,
SyntheticEvent,
useCallback,
} from "react";
+import { Tooltip, useTooltip } from "@finos/vuu-popups";
+import { useId } from "@finos/vuu-layout";
+
+import "./VuuInput.css";
const classBase = "vuuInput";
+const constantInputProps = {
+ autoComplete: "off",
+};
+
export type Commithandler = (
evt: SyntheticEvent,
value: T
@@ -18,6 +28,7 @@ export type Commithandler = (
export interface VuuInputProps<
T extends VuuRowDataItemType = VuuRowDataItemType
> extends InputProps {
+ errorMessage?: string;
onCommit: Commithandler;
type?: T;
}
@@ -26,13 +37,26 @@ export interface VuuInputProps<
* A variant of Salt Input that provides a commit callback prop,
* TODO along with cancel behaviour ?
*/
-export const VuuInput = ({
- className,
- onCommit,
- onKeyDown,
- type,
- ...props
-}: VuuInputProps) => {
+export const VuuInput = forwardRef(function VuuInput<
+ T extends VuuRowDataItemType = string
+>(
+ {
+ className,
+ errorMessage,
+ id: idProp,
+ onCommit,
+ onKeyDown,
+ type,
+ ...props
+ }: VuuInputProps,
+ forwardedRef: ForwardedRef
+) {
+ const id = useId(idProp);
+ const { anchorProps, tooltipProps } = useTooltip({
+ id,
+ tooltipContent: errorMessage,
+ });
+
const commitValue = useCallback>(
(evt, value) => {
if (type === "number") {
@@ -73,12 +97,32 @@ export const VuuInput = ({
[commitValue]
);
- return (
-
+ ) : undefined;
+
+ return (
+ <>
+
+ {tooltipProps ? : null}
+ >
);
-};
+});
diff --git a/vuu-ui/packages/vuu-utils/src/column-utils.ts b/vuu-ui/packages/vuu-utils/src/column-utils.ts
index 3bb469015..89eb46036 100644
--- a/vuu-ui/packages/vuu-utils/src/column-utils.ts
+++ b/vuu-ui/packages/vuu-utils/src/column-utils.ts
@@ -1,10 +1,11 @@
+import type { SchemaColumn, TableSchema } from "@finos/vuu-data";
import type { DataSourceFilter, DataSourceRow } from "@finos/vuu-data-types";
import type {
ColumnAlignment,
ColumnDescriptor,
ColumnType,
ColumnTypeDescriptor,
- ColumnTypeRenderer,
+ ColumnTypeRendering,
ColumnTypeWithValidationRules,
GroupColumnDescriptor,
KeyedColumnDescriptor,
@@ -12,7 +13,9 @@ import type {
PinLocation,
TableHeading,
TableHeadings,
- TypeFormatting,
+ ColumnTypeFormatting,
+ LookupRenderer,
+ ValueListRenderer,
} from "@finos/vuu-datagrid-types";
import type { Filter, MultiClauseFilter } from "@finos/vuu-filter-types";
import type {
@@ -24,9 +27,9 @@ import type {
VuuRowRecord,
VuuSort,
} from "@finos/vuu-protocol-types";
-import type { SchemaColumn } from "@finos/vuu-data";
+import { DefaultColumnConfiguration } from "@finos/vuu-shell";
import type { CSSProperties } from "react";
-import type { CellRendererDescriptor } from "./component-registry";
+import { moveItem } from "./array-utils";
import { isFilterClause, isMultiClauseFilter } from "./filter-utils";
/**
@@ -164,6 +167,9 @@ export declare type ColumnTypeSimple =
| "time"
| "checkbox";
+/**
+ *
+ */
export const isTypeDescriptor = (
type?: ColumnType
): type is ColumnTypeDescriptor =>
@@ -173,8 +179,20 @@ const EMPTY_COLUMN_MAP = {} as const;
export const isColumnTypeRenderer = (
renderer?: unknown
-): renderer is ColumnTypeRenderer =>
- typeof (renderer as ColumnTypeRenderer)?.name !== "undefined";
+): renderer is ColumnTypeRendering =>
+ typeof (renderer as ColumnTypeRendering)?.name !== "undefined";
+
+export const isLookupRenderer = (
+ renderer?: unknown
+): renderer is LookupRenderer =>
+ typeof (renderer as LookupRenderer)?.name !== "undefined" &&
+ "lookup" in (renderer as LookupRenderer);
+
+export const isValueListRenderer = (
+ renderer?: unknown
+): renderer is ValueListRenderer =>
+ typeof (renderer as ValueListRenderer)?.name !== "undefined" &&
+ Array.isArray((renderer as ValueListRenderer).values);
export const hasValidationRules = (
type?: ColumnType
@@ -614,10 +632,10 @@ export const findColumn = (
}
};
-export function updateColumn(
- columns: KeyedColumnDescriptor[],
- column: KeyedColumnDescriptor
-): KeyedColumnDescriptor[];
+export function updateColumn(
+ columns: T[],
+ column: T
+): T[];
export function updateColumn(
columns: KeyedColumnDescriptor[],
column: string,
@@ -747,7 +765,7 @@ export const getDefaultColumnType = (
export const updateColumnType = (
column: T,
- formatting: TypeFormatting
+ formatting: ColumnTypeFormatting
): T => {
const { serverDataType, type = getDefaultColumnType(serverDataType) } =
column;
@@ -771,11 +789,11 @@ export const updateColumnType = (
}
};
-export const updateColumnRenderer = <
+export const updateColumnRenderProps = <
T extends ColumnDescriptor = ColumnDescriptor
>(
column: T,
- cellRenderer: CellRendererDescriptor
+ renderer: ColumnTypeRendering
): T => {
const { serverDataType, type } = column;
if (type === undefined) {
@@ -783,9 +801,7 @@ export const updateColumnRenderer = <
...column,
type: {
name: getDefaultColumnType(serverDataType),
- renderer: {
- name: cellRenderer.name,
- },
+ renderer,
},
};
} else if (isSimpleColumnType(type)) {
@@ -793,9 +809,7 @@ export const updateColumnRenderer = <
...column,
type: {
name: type,
- renderer: {
- name: cellRenderer.name,
- },
+ renderer,
},
};
} else {
@@ -803,18 +817,17 @@ export const updateColumnRenderer = <
...column,
type: {
...type,
- renderer: {
- name: cellRenderer.name,
- },
+ // TODO do we need to preserve any existing attributes from renderer ?
+ renderer,
},
};
}
};
const NO_TYPE_SETTINGS = {};
-export const getTypeSettingsFromColumn = (
+export const getTypeFormattingFromColumn = (
column: ColumnDescriptor
-): TypeFormatting => {
+): ColumnTypeFormatting => {
if (isTypeDescriptor(column.type)) {
return column.type.formatting ?? NO_TYPE_SETTINGS;
} else {
@@ -936,3 +949,40 @@ export const setCalculatedColumnExpression = (
name: `${name}:${type}:=${expression}`,
};
};
+
+export const moveColumnTo = (
+ columns: ColumnDescriptor[],
+ column: ColumnDescriptor,
+ newIndex: number
+) => {
+ const index = columns.indexOf(column);
+ return moveItem(columns, index, newIndex);
+};
+
+export function replaceColumn(
+ state: KeyedColumnDescriptor[],
+ column: KeyedColumnDescriptor
+) {
+ return state.map((col) => (col.name === column.name ? column : col));
+}
+
+export const applyDefaultColumnConfig = (
+ { columns, table }: TableSchema,
+ getDefaultColumnConfig?: DefaultColumnConfiguration
+) => {
+ if (typeof getDefaultColumnConfig === "function") {
+ return columns.map((column) => {
+ const config = getDefaultColumnConfig(table.table, column.name);
+ if (config) {
+ return {
+ ...column,
+ ...config,
+ };
+ } else {
+ return column;
+ }
+ });
+ } else {
+ return columns;
+ }
+};
diff --git a/vuu-ui/packages/vuu-utils/src/component-registry.ts b/vuu-ui/packages/vuu-utils/src/component-registry.ts
index 388bf41e0..99b2dc3f6 100644
--- a/vuu-ui/packages/vuu-utils/src/component-registry.ts
+++ b/vuu-ui/packages/vuu-utils/src/component-registry.ts
@@ -1,6 +1,8 @@
import { FunctionComponent as FC, HTMLAttributes } from "react";
import {
- ColumnTypeRenderer,
+ ColumnDescriptor,
+ ColumnDescriptorCustomRenderer,
+ ColumnTypeRendering,
EditValidationRule,
MappedValueTypeRenderer,
TableCellRendererProps,
@@ -9,13 +11,30 @@ import {
VuuColumnDataType,
VuuRowDataItemType,
} from "@finos/vuu-protocol-types";
+import { isTypeDescriptor, isColumnTypeRenderer } from "./column-utils";
export interface CellConfigPanelProps extends HTMLAttributes {
onConfigChange: () => void;
}
+export type PropertyChangeHandler = (
+ propertyName: string,
+ propertyValue: string | number | boolean
+) => void;
+
+export type ColumnRenderPropsChangeHandler = (
+ renderProps: ColumnTypeRendering
+) => void;
+export interface ConfigurationEditorProps {
+ column: ColumnDescriptorCustomRenderer;
+ onChangeRendering: ColumnRenderPropsChangeHandler;
+}
+
+export type ConfigEditorComponent = FC;
+
const cellRenderersMap = new Map>();
-const cellConfigPanelsMap = new Map>();
+const configEditorsMap = new Map>();
+const cellConfigPanelsMap = new Map();
const editRuleValidatorsMap = new Map();
const optionsMap = new Map();
@@ -30,7 +49,8 @@ export type ComponentType =
| "data-edit-validator";
type CellRendererOptions = {
- [key: string]: unknown;
+ // [key: string]: unknown;
+ configEditor?: string;
description?: string;
label?: string;
serverDataType?: VuuColumnDataType | VuuColumnDataType[] | "json" | "private";
@@ -96,6 +116,13 @@ export function registerComponent<
}
}
+export const registerConfigurationEditor = (
+ componentName: string,
+ configurationEditor: FC
+) => {
+ configEditorsMap.set(componentName, configurationEditor);
+};
+
export const getRegisteredCellRenderers = (
serverDataType?: VuuColumnDataType | "json"
): CellRendererDescriptor[] => {
@@ -113,12 +140,26 @@ export const getRegisteredCellRenderers = (
}
};
-export function getCellRenderer(
- renderer?: ColumnTypeRenderer | MappedValueTypeRenderer
-) {
- if (renderer && "name" in renderer) {
- return cellRenderersMap.get(renderer.name);
+export const getCellRendererOptions = (renderName: string) =>
+ optionsMap.get(renderName);
+
+export function getCellRenderer(column: ColumnDescriptor) {
+ if (isTypeDescriptor(column.type)) {
+ const { renderer } = column.type;
+ if (isColumnTypeRenderer(renderer)) {
+ return cellRenderersMap.get(renderer.name);
+ }
}
+ if (column.editable) {
+ // we can only offer a text input edit as a generic editor.
+ // If a more specialised editor is required, user must configure
+ // it in column config.
+ return cellRenderersMap.get("input-cell");
+ }
+}
+
+export function getConfigurationEditor(configEditor = "") {
+ return configEditorsMap.get(configEditor);
}
export function getCellConfigPanelRenderer(name: string) {
diff --git a/vuu-ui/packages/vuu-utils/src/formatting-utils.ts b/vuu-ui/packages/vuu-utils/src/formatting-utils.ts
index 687adb639..c9dac851c 100644
--- a/vuu-ui/packages/vuu-utils/src/formatting-utils.ts
+++ b/vuu-ui/packages/vuu-utils/src/formatting-utils.ts
@@ -1,7 +1,7 @@
import {
ColumnDescriptor,
ColumnTypeValueMap,
- TypeFormatting,
+ ColumnTypeFormatting,
} from "@finos/vuu-datagrid-types";
import { roundDecimal } from "./round-decimal";
import {
@@ -16,7 +16,7 @@ export type ValueFormatters = {
[key: string]: ValueFormatter;
};
-const DEFAULT_NUMERIC_FORMAT: TypeFormatting = {};
+const DEFAULT_NUMERIC_FORMAT: ColumnTypeFormatting = {};
export const defaultValueFormatter = (value: unknown) =>
value == null ? "" : typeof value === "string" ? value : value.toString();
diff --git a/vuu-ui/packages/vuu-utils/src/json-utils.ts b/vuu-ui/packages/vuu-utils/src/json-utils.ts
index 71e505675..0ddc5f1ac 100644
--- a/vuu-ui/packages/vuu-utils/src/json-utils.ts
+++ b/vuu-ui/packages/vuu-utils/src/json-utils.ts
@@ -41,6 +41,12 @@ const getCellValue = (
): CellValue => {
if (isJsonData(attributeValue)) {
return { attribute: `${attribute}+`, attributeValue: "", type: "json" };
+ } else if (attributeValue === undefined) {
+ return {
+ attribute,
+ attributeValue: "undefined",
+ type: "string",
+ };
} else if (isVuuRowDataItem(attributeValue)) {
return {
attribute,
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/config/localhost.config.json b/vuu-ui/sample-apps/app-vuu-basket-trader/config/localhost.config.json
deleted file mode 100644
index 0967ef424..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/config/localhost.config.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/demo.tsx b/vuu-ui/sample-apps/app-vuu-basket-trader/demo.tsx
deleted file mode 100644
index b732a2f03..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/demo.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from "react";
-import ReactDOM from "react-dom";
-import { LoginPanel } from "@finos/vuu-shell";
-import { SaltProvider } from "@salt-ds/core";
-import { uuid } from "@finos/vuu-utils";
-
-import "./login.css";
-
-async function login(username: string) {
- try {
- const authToken = uuid();
- const date = new Date();
- const days = 1;
- date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
- document.cookie = `vuu-username=${username};expires=${date.toUTCString()};path=/`;
- document.cookie = `vuu-auth-token=${authToken};expires=${date.toUTCString()};path=/`;
- document.cookie = `vuu-auth-mode=demo`;
-
- window.location.href = "index.html";
- } catch (err) {
- console.error(err);
- }
-}
-
-ReactDOM.render(
-
-
- ,
- document.getElementById("root")
-);
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/index.tsx b/vuu-ui/sample-apps/app-vuu-basket-trader/index.tsx
deleted file mode 100644
index e2cd5be6f..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/index.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import {
- getAuthDetailsFromCookies,
- LayoutManagementProvider,
- redirectToLogin,
-} from "@finos/vuu-shell";
-import React from "react";
-import ReactDOM from "react-dom";
-import { App } from "./src/App";
-
-import "@finos/vuu-icons/index.css";
-import "@finos/vuu-theme/index.css";
-
-const [username, token] = getAuthDetailsFromCookies();
-if (!username || !token) {
- // This won't be needed with serverside protection
- redirectToLogin();
-} else {
- ReactDOM.render(
-
-
- ,
- document.getElementById("root")
- );
-}
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/layout-loadind.md b/vuu-ui/sample-apps/app-vuu-basket-trader/layout-loadind.md
deleted file mode 100644
index 17298ff83..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/layout-loadind.md
+++ /dev/null
@@ -1,215 +0,0 @@
-1. client logs in for first time
- /api/vui/{username}
-
-1A) existing service responds with 404, so we display defaultLayout, with placeholder
-
-**defaultLayout JSON is hardcoded in the app**
-
-```JSON
-{
-
-type: "Stack",
-props: {
- style: {
- width: "100%",
- height: "100%",
- },
- enableAddTab: true,
- enableRemoveTab: true,
- preserve: true,
- active: 0,
- TabstripProps: {
- allowAddTab: true,
- allowCloseTab: true,
- allowRenameTab: true,
- },
-},
-children: [
- {
- type: "Placeholder",
- title: "Page 1",
- },
-]
-}
-```
-
-1B) new service returns defaultLayout, with placeholder "vuu.layout.version": 2
-
-Note: we wrap the layout structure with an enclosing envelope that carries the version. We might
-eventually want to record additional metadata here or user settings etc. Maybe application id
-
-Note also: the 'placeholder' is defined inline. I think the new system should equally support
-inline layouts and dynamically loaded layouts. The former should be considered readonly
-
-```JSON
-{
-"vuu.layout.version": 2
-layout:
- {
-
- type: "Stack",
- props: {
- style: {
- width: "100%",
- height: "100%",
- },
- enableAddTab: true,
- enableRemoveTab: true,
- preserve: true,
- active: 0,
- TabstripProps: {
- allowAddTab: true,
- allowCloseTab: true,
- allowRenameTab: true,
- },
- },
- children: [
- {
- type: "Placeholder",
- title: "Page 1",
- },
- ]
- }
-}
-```
-
-2. UI renders defaultLayout, with placeholder. In either of the scenarios above we reach this point.
-
-3. User drags content onto layout to replace or displace placeholder
-
-3A) existing service POSTS to /api/vui/{username} the full persisted JSON structure, as above but with new content in open tab
-
-```JSON
-{
-
-type: "Stack",
-props: {
- style: {
- width: "100%",
- height: "100%",
- },
- enableAddTab: true,
- enableRemoveTab: true,
- preserve: true,
- active: 0,
- TabstripProps: {
- allowAddTab: true,
- allowCloseTab: true,
- allowRenameTab: true,
- },
-},
-children: [
- {
- type: "View",
- id: 'xzy',
- title: "Page 1",
- props: {
- closeable: true,
- header: true,
- label: "SIMUL Instruments",
- resize: "defer"
- },
- state: {
- "table-config": {
- columns: [
- {name: "ric", label: "RIC", pin: "left" },
- ...
- ]
- }
- },
- children: [
- {
- type: "Feature",
- props: {
- url: "./feature-vuu-table/index.js"
- css: "./feature-vuu-table/index.css"
- }
-
- }
- ]
- },
-]
-}
-```
-
-3B) new service makes 2 POST requests
-
-Save the application-level JSON ...
-
-- /api/vui/{username}
-
-```JSON
-{
-"vuu.layout.version": 2
-layout:
- {
-
- type: "Stack",
- props: {
- style: {
- width: "100%",
- height: "100%",
- },
- enableAddTab: true,
- enableRemoveTab: true,
- preserve: true,
- active: 0,
- TabstripProps: {
- allowAddTab: true,
- allowCloseTab: true,
- allowRenameTab: true,
- },
- },
- children: [
- {
- type: "layout",
- title: "Page 1",
- url: "/api/vui/{username}/xyz"
- },
- ]
- }
-}
-```
-
-save the new layout ...
-
-- /api/vui/layouts/xyz
-
-```JSON
-{
- id: "xyz"
- createdBy: "",
- createdTime: "",
- lastUpdatedTime: "",
- layout: {
- type: "View",
- id: 'xzy',
- title: "Page 1",
- props: {
- closeable: true,
- header: true,
- label: "SIMUL Instruments",
- resize: "defer"
- },
- state: {
- "table-config": {
- columns: [
- {name: "ric", label: "RIC", pin: "left" },
- ...
- ]
- }
- },
- children: [
- {
- type: "Feature",
- props: {
- url: "./feature-vuu-table/index.js"
- css: "./feature-vuu-table/index.css"
- }
-
- }
- ]
- }
-}
-
-```
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/login.css b/vuu-ui/sample-apps/app-vuu-basket-trader/login.css
deleted file mode 100644
index 56812898f..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/login.css
+++ /dev/null
@@ -1,16 +0,0 @@
-body {
- align-items: center;
- display: flex;
- flex-direction: column;
- justify-content: center;
- margin: 0;
-}
-
-#root {
- width: 100vw;
- height: 100vh;
- display: flex;
- align-items: center;
- justify-content: center;
-
-}
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/login.tsx b/vuu-ui/sample-apps/app-vuu-basket-trader/login.tsx
deleted file mode 100644
index f2e0e3cee..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/login.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from "react";
-import ReactDOM from "react-dom";
-import { LoginPanel, ThemeProvider } from "@finos/vuu-shell";
-import { authenticate } from "@finos/vuu-data";
-
-import "@finos/vuu-theme/index.css";
-import "./login.css";
-
-async function login(username: string, password: string) {
- try {
- const { authUrl } = await vuuConfig;
- const authToken = await authenticate(username, password, authUrl);
- const date = new Date();
- const days = 1;
- date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
- document.cookie = `vuu-username=${username};expires=${date.toUTCString()};path=/`;
- document.cookie = `vuu-auth-token=${authToken};expires=${date.toUTCString()};path=/`;
- document.cookie = `vuu-auth-mode=login`;
- window.location.href = "index.html";
- } catch (err) {
- console.error(err);
- }
-}
-
-ReactDOM.render(
-
-
- ,
- document.getElementById("root")
-);
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/package.json b/vuu-ui/sample-apps/app-vuu-basket-trader/package.json
deleted file mode 100644
index 8baea4e7b..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/package.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "name": "app-vuu-basket-trader",
- "version": "0.0.26",
- "description": "",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
- "build": "node ./scripts/build.mjs",
- "build:with-ag-grid": "node ./scripts/build.mjs --features feature-vuu-blotter,feature-ag-grid",
- "start": "serve -p 5002 ../../deployed_apps/app-vuu-example"
- },
- "keywords": [],
- "author": "heswell",
- "license": "Apache-2.0",
- "sideEffects": [
- "**/*.css"
- ],
- "devDependencies": {},
- "dependencies": {
- "@fontsource/open-sans": "^4.5.13",
- "@salt-ds/core": "1.8.0",
- "@salt-ds/lab": "1.0.0-alpha.15",
- "@finos/vuu-data": "0.0.26",
- "@finos/vuu-datagrid-types": "0.0.26",
- "@finos/vuu-data-react": "0.0.26",
- "@finos/vuu-layout": "0.0.26",
- "@finos/vuu-popups": "0.0.26",
- "@finos/vuu-shell": "0.0.26",
- "@finos/vuu-utils": "0.0.26",
- "classnames": "^2.3.1",
- "react": "^17.0.2",
- "react-dom": "^17.0.2"
- },
- "engines": {
- "node": ">=16.0.0"
- }
-}
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/about.txt b/vuu-ui/sample-apps/app-vuu-basket-trader/public/about.txt
deleted file mode 100644
index dc353a4a5..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/public/about.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This favicon was generated using the following font:
-
-- Font Title: Roboto Condensed
-- Font Author: Copyright 2011 Google Inc. All Rights Reserved.
-- Font Source: http://fonts.gstatic.com/s/robotocondensed/v19/ieVl2ZhZI2eCN5jzbjEETS9weq8-59WxDCs5cvI.ttf
-- Font License: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html))
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/apple-touch-icon.png b/vuu-ui/sample-apps/app-vuu-basket-trader/public/apple-touch-icon.png
deleted file mode 100644
index 74bdb1c5c..000000000
Binary files a/vuu-ui/sample-apps/app-vuu-basket-trader/public/apple-touch-icon.png and /dev/null differ
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/demo.html b/vuu-ui/sample-apps/app-vuu-basket-trader/public/demo.html
deleted file mode 100644
index 5e53d03ff..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/public/demo.html
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- VUU App
-
-
-
-
-
-
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.ico b/vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.ico
deleted file mode 100644
index cc26fb39d..000000000
Binary files a/vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.ico and /dev/null differ
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/index.html b/vuu-ui/sample-apps/app-vuu-basket-trader/public/index.html
deleted file mode 100644
index 155bead1d..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/public/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- VUU App
-
-
-
-
-
-
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/login.html b/vuu-ui/sample-apps/app-vuu-basket-trader/public/login.html
deleted file mode 100644
index d9fcdf50c..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/public/login.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- VUU App
-
-
-
-
-
-
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/manifest.json b/vuu-ui/sample-apps/app-vuu-basket-trader/public/manifest.json
deleted file mode 100644
index 17ddafb82..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/public/manifest.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "",
- "short_name": "",
- "theme_color": "#ffffff",
- "background_color": "#ffffff",
- "display": "standalone"
-}
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/scripts/.eslintrc.json b/vuu-ui/sample-apps/app-vuu-basket-trader/scripts/.eslintrc.json
deleted file mode 100644
index 17388657e..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/scripts/.eslintrc.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "env": {
- "node": true,
- "es2021": true
- },
- "extends": ["eslint:recommended", "plugin:react/recommended"],
- "parserOptions": {
- "ecmaFeatures": {
- "jsx": true
- },
- "ecmaVersion": "latest",
- "sourceType": "module"
- },
- "rules": {}
-}
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/scripts/build.mjs b/vuu-ui/sample-apps/app-vuu-basket-trader/scripts/build.mjs
deleted file mode 100644
index 3e83b8134..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/scripts/build.mjs
+++ /dev/null
@@ -1,191 +0,0 @@
-import {
- assertFileExists,
- byFileName,
- copyFolderSync,
- formatBytes,
- formatDuration,
- getCommandLineArg,
- padRight,
- readJson,
- readPackageJson,
- writeMetaFile,
-} from "../../../scripts/utils.mjs";
-import { build } from "../../../scripts/esbuild.mjs";
-import fs from "fs";
-import path from "path";
-
-const entryPoints = ["index.tsx", "login.tsx", "demo.tsx"];
-
-const outdir = "../../deployed_apps/app-vuu-basket-trader";
-let configFile = "./config/localhost.config.json";
-
-const websocketUrl = getCommandLineArg("--url", true);
-const watch = getCommandLineArg("--watch");
-const development = watch || getCommandLineArg("--dev");
-const configPath = getCommandLineArg("--config", true);
-const features = getCommandLineArg(
- "--features",
- true,
- "feature-filter-table,feature-instrument-tiles,feature-basket-trading"
-);
-if (configPath) {
- configFile = configPath;
-}
-
-const featureEntryPoints = features
- .split(",")
- .map((featureName) => `../${featureName}/index.ts`);
-
-assertFileExists(configFile, true);
-
-const { name: projectName } = readPackageJson();
-
-const esbuildConfig = {
- entryPoints: entryPoints.concat(featureEntryPoints),
- env: development ? "development" : "production",
- name: "app-vuu-basket-trader",
- outdir,
- splitting: true,
- target: "esnext",
-};
-
-async function writeFeatureEntriesToConfigJson(featureBundles) {
- return new Promise((resolve, reject) => {
- console.log("[DEPLOY config]");
- const configJson = readJson(configFile);
- if (websocketUrl) {
- configJson.websocketUrl = websocketUrl;
- }
- let { features } = configJson;
- if (features === undefined) {
- features = configJson.features = {};
- }
-
- const featureFilePath = (featureName, files, matchPattern) => {
- const file = files.find(({ fileName }) =>
- fileName.endsWith(matchPattern)
- );
- if (file) {
- return `./feature-${featureName}/${file.fileName}`;
- }
- };
-
- featureBundles.forEach(({ name, files }) => {
- const { description = name, vuu } = readJson(
- path.resolve(`../feature-${name}/package.json`)
- );
- features[name] = {
- title: description,
- name,
- url: featureFilePath(name, files, ".js"),
- css: featureFilePath(name, files, ".css"),
- ...vuu,
- };
- });
-
- fs.writeFile(
- path.resolve(outdir, "config.json"),
- JSON.stringify(configJson, null, 2),
- (err) => {
- if (err) {
- reject(err);
- } else {
- resolve();
- }
- }
- );
- });
-}
-
-async function main() {
- function createDeployFolder() {
- fs.rmSync(outdir, { recursive: true, force: true });
- fs.mkdirSync(outdir, { recursive: true });
- }
-
- console.log("[CLEAN]");
- createDeployFolder();
-
- console.log("[BUILD]");
- const [
- {
- result: { metafile },
- duration,
- },
- ] = await Promise.all([build(esbuildConfig)]).catch((e) => {
- console.error(e);
- process.exit(1);
- });
-
- await writeMetaFile(metafile, outdir);
-
- console.log("[DEPLOY public assets]");
- const publicContent = fs.readdirSync(`./public`);
- publicContent.forEach((file) => {
- if (file !== ".DS_Store") {
- if (typeof fs.cp === "function") {
- // node v16.7 +
- fs.cp(
- path.resolve("public", file),
- path.resolve(outdir, file),
- { recursive: true },
- (err) => {
- if (err) throw err;
- }
- );
- } else {
- // delete once we no longer need to support node16 < .7
- copyFolderSync(
- path.resolve("public", file),
- path.resolve(outdir, file)
- );
- }
- }
- });
-
- const outputs = {
- core: [],
- common: [],
- features: [],
- };
- for (const [file, { bytes }] of Object.entries(metafile.outputs)) {
- if (file.endsWith("js") || file.endsWith("css")) {
- const fileName = file.replace(`${outdir}/`, "");
- if (fileName.startsWith(projectName)) {
- outputs.core.push({ fileName, bytes });
- } else if (fileName.startsWith("feature")) {
- const [name, featureFileName] = fileName.split("/");
- const featureName = name.replace("feature-", "");
- let feature = outputs.features.find((f) => f.name === featureName);
- if (feature === undefined) {
- feature = { name: featureName, files: [] };
- outputs.features.push(feature);
- }
- feature.files.push({ fileName: featureFileName, bytes });
- } else {
- outputs.common.push({ fileName, bytes });
- }
- }
- }
-
- console.log("\ncore");
- outputs.core.sort(byFileName).forEach(({ fileName, bytes }) => {
- console.log(`${padRight(fileName, 30)} ${formatBytes(bytes)}`);
- });
- console.log("\ncommon");
- outputs.common.forEach(({ fileName, bytes }) => {
- console.log(`${padRight(fileName, 30)} ${formatBytes(bytes)}`);
- });
- outputs.features.forEach(({ name, files }) => {
- console.log(`\nfeature: ${name}`);
- files.forEach(({ fileName, bytes }) => {
- console.log(`${padRight(fileName, 30)} ${formatBytes(bytes)}`);
- });
- });
-
- console.log(`\nbuild took ${formatDuration(duration)}`);
-
- await writeFeatureEntriesToConfigJson(outputs.features);
-}
-
-main();
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.css b/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.css
deleted file mode 100644
index 52c514dcd..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.css
+++ /dev/null
@@ -1,157 +0,0 @@
-body {
- padding: 0;
- margin: 0;
- height: 100vh;
- width: 100vw;
- overflow: hidden;
-}
-
-.ToolbarField .Input {
- border: solid 1px #ccc;
- height: 28px;
- padding: 0 8px;
-}
-
-.DragIcon.dragging {
- background-color: orange !important;
-}
-
-.vuuApp {
- background-color: green;
-}
-
-.vuuShell-content {
- padding: 8px;
-}
-
-
-.ToolbarField > svg {
- fill: var(--spectrum-global-color-static-gray-600);
- width: 18px;
- height: 18px;
-}
-
-.vuHeaderCell {
- text-transform: uppercase;
-}
-
-.vuDialog {
- margin-top: 20%;
-}
-
-.vuDialog {
- --vuuView-flex-direction: row;
- --vuuView-flex-wrap: wrap;
-}
-
-.vuDialog .Grid {
- --hw-grid-flex-size: 1 1 auto;
-}
-
-/** Temp, until we create Toolbar */
-.vuuToolbarProxy {
- align-items: center;
- display: flex;
- gap: 12px;
- height: var(--vuuToolbarProxy-height, 36px);
-}
-
-.vuuToolbarProxy > [data-align="end"]{
- margin-left: auto;
-}
-
-.vuuToolbarProxy-vertical {
- flex-direction: column;
-}
-
-.vuuShell-mainTabs {
- --vuuTab-height: 28px;
- border: solid 1px #D6D7DA;
- border-top: none !important;
- border-radius: 6px;
- height: 100%;
- padding: 36px 8px 8px 8px;
- position: relative;
- width: 100%;
-}
-
-.vuuShellMainTabstrip > .vuuOverflowContainer-wrapContainer {
- background: var(--vuu-color-gray-25);
-
-}
-
- .vuuShell-mainTabs > .vuuTabstrip {
- --vuuTabstrip-height: 28px;
- --saltTabs-tabstrip-height: 29px;
- --tabstrip-height: 29px;
- left:-1px;
- padding-bottom: 7px;
- position: absolute !important;
- right: 1px;
- top: 0;
- width: calc(100% + 2px) !important;
- }
-
- .vuuShell-mainTabs > .vuuTabHeader {
- border-bottom: none;
- }
-
- .vuuShell-mainTabs > .vuuTabstrip:before {
- background-color: transparent;
- border-radius: 0 6px 0 0;
- border-left: solid 1px #D6D7DA;
- border-right: solid 1px #D6D7DA;
- border-top: solid 1px #D6D7DA;
- content: '';
- position: absolute;
- bottom: 0;
- left:0;
- right:0;
- height: 8px;
- z-index: 1;
- }
-
- .vuuTab.MainTab {
- background-color: #F1F2F4;
- border-color: #D6D7DA;
- border-radius: 6px 6px 0 0;
- border-width: 1px;
- border-style: solid;
- position: relative;
- }
-
- .MainTab.vuuTab-selected {
- background-color: white;
- border-bottom-color: white;
- z-index: 1;
-
- }
-
- .MainTab.vuuTab-selected:before{
- background-color: #6d188b;;
- content: '';
- position: absolute;
- height: 100%;
- left:0;
- top:0;
- border-radius: 6px 0 0 0;
- width: 6px;
- }
-
- .MainTab.vuuTab:hover:not(.vuuTab-selected):before{
- background-color: #F37880;
- content: '';
- position: absolute;
- height: 100%;
- left:0;
- top:0;
- border-radius: 6px 0 0 0;
- width: 6px;
- }
-
- .vuuTab.MainTab .vuuTab-main {
- background-color: transparent;
- font-weight: 700;
- height: 29px;
- padding: 0 24px;
- }
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.tsx b/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.tsx
deleted file mode 100644
index 332e584be..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import { ContextMenuProvider, useDialog } from "@finos/vuu-popups";
-import {
- LeftNav,
- Shell,
- ShellContextProvider,
- ShellProps,
- VuuUser,
-} from "@finos/vuu-shell";
-import { getDefaultColumnConfig } from "./columnMetaData";
-import { createPlaceholder } from "./createPlaceholder";
-import { useFeatures } from "./useFeatures";
-import {
- ColumnSettingsPanel,
- TableSettingsPanel,
-} from "@finos/vuu-table-extras";
-import {
- registerComponent,
- useLayoutContextMenuItems,
-} from "@finos/vuu-layout";
-
-import "./App.css";
-import { useRpcResponseHandler } from "./useRpcResponseHandler";
-
-registerComponent("ColumnSettings", ColumnSettingsPanel, "view");
-registerComponent("TableSettings", TableSettingsPanel, "view");
-
-// createNewChild is used when we add a new Tab to Stack
-const layoutProps: ShellProps["LayoutProps"] = {
- createNewChild: createPlaceholder,
- pathToDropTarget: "#main-tabs.ACTIVE_CHILD",
-};
-
-const defaultWebsocketUrl = `wss://${location.hostname}:8090/websocket`;
-const {
- websocketUrl: serverUrl = defaultWebsocketUrl,
- features: configuredFeatures,
-} =
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- await vuuConfig;
-
-export const App = ({ user }: { user: VuuUser }) => {
- const [features, tableFeatures] = useFeatures({
- features: configuredFeatures,
- });
-
- const { dialog, setDialogState } = useDialog();
- const { handleRpcResponse } = useRpcResponseHandler(setDialogState);
- const { buildMenuOptions, handleMenuAction } =
- useLayoutContextMenuItems(setDialogState);
-
- // TODO get Context from Shell
- return (
-
-
-
- }
- saveUrl="https://localhost:8443/api/vui"
- serverUrl={serverUrl}
- user={user}
- >
- {dialog}
-
-
-
- );
-};
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/Layouts.jsx b/vuu-ui/sample-apps/app-vuu-basket-trader/src/Layouts.jsx
deleted file mode 100644
index 4e95fc3f7..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/src/Layouts.jsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from "react";
-import { FlexboxLayout as Flexbox, Placeholder } from "@finos/vuu-layout";
-
-export const twoColumns = (
-
-
-
-
-);
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/columnMetaData.ts b/vuu-ui/sample-apps/app-vuu-basket-trader/src/columnMetaData.ts
deleted file mode 100644
index 7a8016780..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/src/columnMetaData.ts
+++ /dev/null
@@ -1,399 +0,0 @@
-import { ColumnDescriptor } from "@finos/vuu-datagrid-types";
-
-const Average = 2;
-
-const ccy: Partial = {
- name: "ccy",
- label: "CCY",
- width: 60,
-};
-
-const filledQuantity: Partial = {
- label: "Filled Qty",
- name: "filledQuantity",
- minWidth: 150,
- type: {
- name: "number",
- renderer: { name: "progress", associatedField: "quantity" },
- formatting: { decimals: 0 },
- },
-};
-
-const ric: Partial = {
- name: "ric",
- label: "RIC",
- type: {
- name: "string",
- },
- width: 60,
-};
-
-const side: Partial = {
- label: "Side",
- name: "side",
- type: {
- name: "string",
- },
- width: 60,
-};
-
-const columnMetaData: { [key: string]: Partial } = {
- account: {
- label: "Account",
- name: "account",
- type: {
- name: "string",
- },
- },
- algo: {
- label: "Algo",
- name: "algo",
- type: {
- name: "string",
- },
- },
- ask: {
- name: "ask",
- label: "Ask",
- type: {
- name: "number",
- renderer: { name: "background", flashStyle: "arrow-bg" },
- formatting: { decimals: 2, zeroPad: true },
- },
- aggregate: Average,
- },
- askSize: {
- name: "askSize",
- label: "Ask Size",
- type: {
- name: "number",
- },
- aggregate: Average,
- },
- averagePrice: {
- label: "Average Price",
- name: "averagePrice",
- type: {
- name: "number",
- },
- aggregate: Average,
- },
- bbg: {
- name: "bbg",
- label: "BBG",
- type: {
- name: "string",
- },
- },
- bid: {
- label: "Bid",
- name: "bid",
- type: {
- name: "number",
- renderer: { name: "background", flashStyle: "arrow-bg" },
- formatting: { decimals: 2, zeroPad: true },
- },
- aggregate: Average,
- },
- bidSize: {
- label: "Bid Size",
- name: "bidSize",
- type: {
- name: "number",
- },
- aggregate: Average,
- },
- childCount: {
- label: "Child Count",
- name: "childCount",
- type: {
- name: "number",
- },
- aggregate: Average,
- },
-
- close: {
- label: "Close",
- name: "close",
- type: {
- name: "number",
- formatting: { decimals: 2, zeroPad: true },
- },
- aggregate: Average,
- },
- clOrderId: {
- label: "Child Order ID",
- name: "clOrderId",
- width: 60,
- },
- created: {
- label: "Created",
- name: "created",
- type: {
- name: "time",
- },
- },
- currency: {
- name: "currency",
- label: "CCY",
- width: 60,
- },
- description: {
- name: "description",
- label: "Description",
- type: {
- name: "string",
- },
- },
- exchange: {
- name: "exchange",
- label: "Exchange",
- type: {
- name: "string",
- },
- },
- filledQty: {
- label: "Filled Qty",
- name: "filledQty",
- width: 150,
- type: {
- name: "number",
- },
- },
- id: {
- name: "id",
- label: "ID",
- type: {
- name: "string",
- },
- },
- idAsInt: {
- label: "ID (int)",
- name: "idAsInt",
- type: {
- name: "string",
- },
- },
- isin: {
- name: "isin",
- label: "ISIN",
- type: {
- name: "string",
- },
- },
- last: {
- label: "Last",
- name: "last",
- type: {
- name: "number",
- formatting: { decimals: 2, zeroPad: true },
- },
- aggregate: Average,
- },
- lastUpdate: {
- label: "Last Update",
- name: "lastUpdate",
- type: {
- name: "time",
- },
- },
- lotSize: {
- label: "Lot Size",
- name: "lotSize",
- width: 80,
- type: {
- name: "number",
- },
- },
- max: {
- label: "Max",
- name: "max",
- width: 80,
- type: {
- name: "number",
- },
- },
- mean: {
- label: "Mean",
- name: "mean",
- width: 80,
- type: {
- name: "number",
- },
- },
- open: {
- label: "Open",
- name: "open",
- type: {
- name: "number",
- formatting: { decimals: 2, zeroPad: true },
- },
- aggregate: Average,
- },
- openQty: {
- label: "Open Qty",
- name: "openQty",
- width: 80,
- type: {
- name: "number",
- formatting: { decimals: 0 },
- },
- },
- orderId: {
- label: "Order ID",
- name: "orderId",
- width: 60,
- },
-
- phase: {
- label: "Phase",
- name: "phase",
- type: {
- name: "string",
- },
- },
- parentOrderId: {
- label: "Parent Order Id",
- name: "parentOrderId",
- width: 80,
- type: {
- name: "number",
- },
- },
- orderType: {
- label: "Order Type",
- name: "orderType",
- type: {
- name: "string",
- },
- },
- price: {
- label: "Price",
- name: "price",
- type: {
- name: "number",
- formatting: { decimals: 2, zeroPad: true },
- },
- aggregate: Average,
- },
- priceLevel: {
- label: "Price Level",
- name: "priceLevel",
- type: {
- name: "string",
- },
- },
- quantity: {
- label: "Quantity",
- name: "quantity",
- width: 80,
- type: {
- name: "number",
- },
- },
- scenario: {
- label: "Scenario",
- name: "scenario",
- type: {
- name: "string",
- },
- },
- size: {
- label: "Size",
- name: "size",
- width: 80,
- type: {
- name: "number",
- },
- },
- status: {
- label: "Status",
- name: "status",
- type: {
- name: "string",
- },
- },
- strategy: {
- label: "Strategy",
- name: "strategy",
- type: {
- name: "string",
- },
- },
- table: {
- label: "Table",
- name: "table",
- type: {
- name: "string",
- },
- },
- trader: {
- label: "Trader",
- name: "trader",
- type: {
- name: "string",
- },
- },
- uniqueId: {
- label: "Unique ID",
- name: "uniqueId",
- type: {
- name: "string",
- },
- },
- updateCount: {
- label: "Update Count",
- name: "updateCount",
- width: 80,
- type: {
- name: "number",
- },
- },
- updatesPerSecond: {
- label: "Updates Per Second",
- name: "updatesPerSecond",
- width: 80,
- type: {
- name: "number",
- },
- },
- user: {
- label: "User",
- name: "user",
- type: {
- name: "string",
- },
- },
- volLimit: {
- label: "Vol Limit",
- name: "volLimit",
- width: 80,
- type: {
- name: "number",
- },
- },
-};
-
-type TableColDefs = { [key: string]: Partial };
-
-const tables: { [key: string]: TableColDefs } = {
- orders: {
- ccy,
- filledQuantity,
- ric,
- side,
- },
- ordersPrices: {
- ccy,
- filledQuantity,
- ric,
- side,
- },
-};
-
-export const getDefaultColumnConfig = (
- tableName: string,
- columnName: string
-) => {
- return tables[tableName]?.[columnName] ?? columnMetaData[columnName];
-};
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/createPlaceholder.tsx b/vuu-ui/sample-apps/app-vuu-basket-trader/src/createPlaceholder.tsx
deleted file mode 100644
index 0bfc5cb22..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/src/createPlaceholder.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Placeholder, View } from "@finos/vuu-layout";
-
-export const createPlaceholder = (index?: number) => (
- // Note make this width 100% and height 100% and we get a weird error where view continually resizes - growing
-
-
-
-);
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/index.ts b/vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/index.ts
deleted file mode 100644
index 8e92f0ebb..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./session-table-config";
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/session-table-config.ts b/vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/session-table-config.ts
deleted file mode 100644
index 91dc3cb01..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/session-table-config.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import {
- MenuRpcResponse,
- OpenDialogAction,
- TableSchema,
-} from "@finos/vuu-data";
-import { FormConfig, FormFieldDescriptor } from "@finos/vuu-shell";
-
-const static_config: { [key: string]: Partial } = {
- OPEN_EDIT_RESET_FIX: {
- title: "Reset the Sequence Number",
- fields: [
- {
- label: "Process Id",
- description: "Process Id",
- name: "process-id",
- type: "string",
- },
- {
- description: "Sequence Number",
- label: "Sequence Number",
- name: "sequenceNumber",
- type: "long",
- },
- ],
- },
-};
-
-const mergeFields = (
- fields: FormFieldDescriptor[],
- staticFields?: FormFieldDescriptor[]
-) => {
- if (Array.isArray(staticFields)) {
- return fields.map((field) => {
- const { name } = field;
- const staticField = staticFields.find((f) => f.name === name);
- if (staticField) {
- return {
- ...field,
- ...staticField,
- };
- } else {
- return field;
- }
- });
- } else {
- return fields;
- }
-};
-
-const getStaticConfig = (rpcName: string, formConfig: FormConfig) => {
- const staticConfig = static_config[rpcName];
- if (staticConfig) {
- return {
- ...formConfig,
- ...staticConfig,
- fields: mergeFields(formConfig.fields, staticConfig.fields),
- };
- } else {
- return formConfig;
- }
-};
-
-const defaultFormConfig = {
- fields: [],
- key: "",
- title: "",
-};
-
-const keyFirst = (c1: FormFieldDescriptor, c2: FormFieldDescriptor) =>
- c1.isKeyField ? -1 : c2.isKeyField ? 1 : 0;
-
-const configFromSchema = (schema?: TableSchema): FormConfig | undefined => {
- if (schema) {
- const { columns, key } = schema;
- return {
- key,
- title: `Parameters for command`,
- fields: columns
- .map((col) => ({
- description: col.name,
- label: col.name,
- name: col.name,
- type: col.serverDataType,
- isKeyField: col.name === key,
- }))
- .sort(keyFirst),
- };
- }
-};
-
-export const getFormConfig = ({ action, rpcName }: MenuRpcResponse) => {
- const { tableSchema: schema } = action as OpenDialogAction;
- const config = configFromSchema(schema) ?? defaultFormConfig;
-
- if (rpcName !== undefined && rpcName in static_config) {
- return {
- config: getStaticConfig(rpcName, config),
- schema,
- };
- }
-
- return {
- config,
- schema,
- };
-};
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/tsconfig.json b/vuu-ui/sample-apps/app-vuu-basket-trader/tsconfig.json
deleted file mode 100644
index 409f9488e..000000000
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/tsconfig.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "extends": "../../tsconfig.json",
- "compilerOptions": {
- "target": "esnext",
- }
-}
diff --git a/vuu-ui/sample-apps/app-vuu-example/demo.tsx b/vuu-ui/sample-apps/app-vuu-example/demo.tsx
index b732a2f03..f82aa7fc9 100644
--- a/vuu-ui/sample-apps/app-vuu-example/demo.tsx
+++ b/vuu-ui/sample-apps/app-vuu-example/demo.tsx
@@ -1,9 +1,11 @@
import React from "react";
import ReactDOM from "react-dom";
-import { LoginPanel } from "@finos/vuu-shell";
-import { SaltProvider } from "@salt-ds/core";
+import { LoginPanel, ThemeProvider } from "@finos/vuu-shell";
import { uuid } from "@finos/vuu-utils";
+import "@finos/vuu-icons/index.css";
+import "@finos/vuu-theme/index.css";
+
import "./login.css";
async function login(username: string) {
@@ -23,8 +25,8 @@ async function login(username: string) {
}
ReactDOM.render(
-
+
- ,
+ ,
document.getElementById("root")
);
diff --git a/vuu-ui/sample-apps/app-vuu-example/index.tsx b/vuu-ui/sample-apps/app-vuu-example/index.tsx
index 770db3202..e2cd5be6f 100644
--- a/vuu-ui/sample-apps/app-vuu-example/index.tsx
+++ b/vuu-ui/sample-apps/app-vuu-example/index.tsx
@@ -1,14 +1,14 @@
-import React from "react";
-import ReactDOM from "react-dom";
-import { App } from "./src/App";
import {
getAuthDetailsFromCookies,
LayoutManagementProvider,
redirectToLogin,
} from "@finos/vuu-shell";
+import React from "react";
+import ReactDOM from "react-dom";
+import { App } from "./src/App";
-import "@salt-ds/theme/index.css";
import "@finos/vuu-icons/index.css";
+import "@finos/vuu-theme/index.css";
const [username, token] = getAuthDetailsFromCookies();
if (!username || !token) {
diff --git a/vuu-ui/sample-apps/app-vuu-example/login.tsx b/vuu-ui/sample-apps/app-vuu-example/login.tsx
index 878194ee0..925da5457 100644
--- a/vuu-ui/sample-apps/app-vuu-example/login.tsx
+++ b/vuu-ui/sample-apps/app-vuu-example/login.tsx
@@ -1,13 +1,14 @@
import React from "react";
import ReactDOM from "react-dom";
-import { LoginPanel } from "@finos/vuu-shell";
+import { LoginPanel, ThemeProvider } from "@finos/vuu-shell";
import { authenticate } from "@finos/vuu-data";
-import { SaltProvider } from "@salt-ds/core";
+import "@finos/vuu-icons/index.css";
import "@finos/vuu-theme/index.css";
+
import "./login.css";
-async function login(username: string, password: string) {
+async function login(username: string, password = "password") {
try {
const { authUrl } = await vuuConfig;
const authToken = await authenticate(username, password, authUrl);
@@ -24,8 +25,8 @@ async function login(username: string, password: string) {
}
ReactDOM.render(
-
+
- ,
+ ,
document.getElementById("root")
);
diff --git a/vuu-ui/sample-apps/app-vuu-example/package.json b/vuu-ui/sample-apps/app-vuu-example/package.json
index c6a74eb97..4367ff480 100644
--- a/vuu-ui/sample-apps/app-vuu-example/package.json
+++ b/vuu-ui/sample-apps/app-vuu-example/package.json
@@ -18,9 +18,7 @@
"dependencies": {
"@fontsource/open-sans": "^4.5.13",
"@salt-ds/core": "1.8.0",
- "@salt-ds/icons": "1.5.1",
"@salt-ds/lab": "1.0.0-alpha.15",
- "@salt-ds/theme": "1.7.1",
"@finos/vuu-data": "0.0.26",
"@finos/vuu-datagrid-types": "0.0.26",
"@finos/vuu-data-react": "0.0.26",
diff --git a/vuu-ui/sample-apps/app-vuu-example/public/.DS_Store b/vuu-ui/sample-apps/app-vuu-example/public/.DS_Store
deleted file mode 100644
index f636ba78d..000000000
Binary files a/vuu-ui/sample-apps/app-vuu-example/public/.DS_Store and /dev/null differ
diff --git a/vuu-ui/sample-apps/app-vuu-example/public/demo.html b/vuu-ui/sample-apps/app-vuu-example/public/demo.html
index 8054d3bf6..9abfbcef2 100644
--- a/vuu-ui/sample-apps/app-vuu-example/public/demo.html
+++ b/vuu-ui/sample-apps/app-vuu-example/public/demo.html
@@ -9,7 +9,6 @@
-
VUU App
diff --git a/vuu-ui/sample-apps/app-vuu-example/public/favicon-16x16.png b/vuu-ui/sample-apps/app-vuu-example/public/favicon-16x16.png
deleted file mode 100644
index f85891bba..000000000
Binary files a/vuu-ui/sample-apps/app-vuu-example/public/favicon-16x16.png and /dev/null differ
diff --git a/vuu-ui/sample-apps/app-vuu-example/public/favicon-32x32.png b/vuu-ui/sample-apps/app-vuu-example/public/favicon-32x32.png
deleted file mode 100644
index 84d627f7a..000000000
Binary files a/vuu-ui/sample-apps/app-vuu-example/public/favicon-32x32.png and /dev/null differ
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.png b/vuu-ui/sample-apps/app-vuu-example/public/favicon.png
similarity index 100%
rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.png
rename to vuu-ui/sample-apps/app-vuu-example/public/favicon.png
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.svg b/vuu-ui/sample-apps/app-vuu-example/public/favicon.svg
similarity index 100%
rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.svg
rename to vuu-ui/sample-apps/app-vuu-example/public/favicon.svg
diff --git a/vuu-ui/sample-apps/app-vuu-example/public/index.html b/vuu-ui/sample-apps/app-vuu-example/public/index.html
index 69b362da4..a6cd74925 100644
--- a/vuu-ui/sample-apps/app-vuu-example/public/index.html
+++ b/vuu-ui/sample-apps/app-vuu-example/public/index.html
@@ -2,9 +2,7 @@
-
-
-
+
@@ -12,8 +10,7 @@
-
-
+
VUU App
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/vuu-icon.svg b/vuu-ui/sample-apps/app-vuu-example/public/vuu-icon.svg
similarity index 100%
rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/vuu-icon.svg
rename to vuu-ui/sample-apps/app-vuu-example/public/vuu-icon.svg
diff --git a/vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs b/vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs
index b91fb1bbf..4a4bec219 100644
--- a/vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs
+++ b/vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs
@@ -20,12 +20,14 @@ const outdir = "../../deployed_apps/app-vuu-example";
let configFile = "./config/localhost.config.json";
const websocketUrl = getCommandLineArg("--url", true);
-console.log(`websocket URL ${websocketUrl} type ${typeof websocketUrl}`);
const watch = getCommandLineArg("--watch");
const development = watch || getCommandLineArg("--dev");
const configPath = getCommandLineArg("--config", true);
-const features = getCommandLineArg("--features", true, "feature-vuu-table");
-console.log({ features });
+const features = getCommandLineArg(
+ "--features",
+ true,
+ "feature-filter-table,feature-instrument-tiles,feature-basket-trading"
+);
if (configPath) {
configFile = configPath;
}
@@ -69,7 +71,7 @@ async function writeFeatureEntriesToConfigJson(featureBundles) {
};
featureBundles.forEach(({ name, files }) => {
- const { description = name } = readJson(
+ const { description = name, vuu } = readJson(
path.resolve(`../feature-${name}/package.json`)
);
features[name] = {
@@ -77,6 +79,7 @@ async function writeFeatureEntriesToConfigJson(featureBundles) {
name,
url: featureFilePath(name, files, ".js"),
css: featureFilePath(name, files, ".css"),
+ ...vuu,
};
});
diff --git a/vuu-ui/sample-apps/app-vuu-example/src/App.css b/vuu-ui/sample-apps/app-vuu-example/src/App.css
index 048d3429c..52c514dcd 100644
--- a/vuu-ui/sample-apps/app-vuu-example/src/App.css
+++ b/vuu-ui/sample-apps/app-vuu-example/src/App.css
@@ -6,12 +6,6 @@ body {
overflow: hidden;
}
-.App {
-
- --vuuView-borderStyle: none solid solid none;
-
-}
-
.ToolbarField .Input {
border: solid 1px #ccc;
height: 28px;
@@ -26,6 +20,11 @@ body {
background-color: green;
}
+.vuuShell-content {
+ padding: 8px;
+}
+
+
.ToolbarField > svg {
fill: var(--spectrum-global-color-static-gray-600);
width: 18px;
@@ -54,7 +53,7 @@ body {
align-items: center;
display: flex;
gap: 12px;
- height: 36px;
+ height: var(--vuuToolbarProxy-height, 36px);
}
.vuuToolbarProxy > [data-align="end"]{
@@ -66,8 +65,93 @@ body {
}
.vuuShell-mainTabs {
+ --vuuTab-height: 28px;
+ border: solid 1px #D6D7DA;
+ border-top: none !important;
+ border-radius: 6px;
height: 100%;
+ padding: 36px 8px 8px 8px;
position: relative;
width: 100%;
}
+.vuuShellMainTabstrip > .vuuOverflowContainer-wrapContainer {
+ background: var(--vuu-color-gray-25);
+
+}
+
+ .vuuShell-mainTabs > .vuuTabstrip {
+ --vuuTabstrip-height: 28px;
+ --saltTabs-tabstrip-height: 29px;
+ --tabstrip-height: 29px;
+ left:-1px;
+ padding-bottom: 7px;
+ position: absolute !important;
+ right: 1px;
+ top: 0;
+ width: calc(100% + 2px) !important;
+ }
+
+ .vuuShell-mainTabs > .vuuTabHeader {
+ border-bottom: none;
+ }
+
+ .vuuShell-mainTabs > .vuuTabstrip:before {
+ background-color: transparent;
+ border-radius: 0 6px 0 0;
+ border-left: solid 1px #D6D7DA;
+ border-right: solid 1px #D6D7DA;
+ border-top: solid 1px #D6D7DA;
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left:0;
+ right:0;
+ height: 8px;
+ z-index: 1;
+ }
+
+ .vuuTab.MainTab {
+ background-color: #F1F2F4;
+ border-color: #D6D7DA;
+ border-radius: 6px 6px 0 0;
+ border-width: 1px;
+ border-style: solid;
+ position: relative;
+ }
+
+ .MainTab.vuuTab-selected {
+ background-color: white;
+ border-bottom-color: white;
+ z-index: 1;
+
+ }
+
+ .MainTab.vuuTab-selected:before{
+ background-color: #6d188b;;
+ content: '';
+ position: absolute;
+ height: 100%;
+ left:0;
+ top:0;
+ border-radius: 6px 0 0 0;
+ width: 6px;
+ }
+
+ .MainTab.vuuTab:hover:not(.vuuTab-selected):before{
+ background-color: #F37880;
+ content: '';
+ position: absolute;
+ height: 100%;
+ left:0;
+ top:0;
+ border-radius: 6px 0 0 0;
+ width: 6px;
+ }
+
+ .vuuTab.MainTab .vuuTab-main {
+ background-color: transparent;
+ font-weight: 700;
+ height: 29px;
+ padding: 0 24px;
+ }
diff --git a/vuu-ui/sample-apps/app-vuu-example/src/App.tsx b/vuu-ui/sample-apps/app-vuu-example/src/App.tsx
index 91562ab06..332e584be 100644
--- a/vuu-ui/sample-apps/app-vuu-example/src/App.tsx
+++ b/vuu-ui/sample-apps/app-vuu-example/src/App.tsx
@@ -1,35 +1,28 @@
-import { hasAction, MenuRpcResponse, TableSchema } from "@finos/vuu-data";
-import { RpcResponseHandler, useVuuTables } from "@finos/vuu-data-react";
-import { Dialog } from "@finos/vuu-popups";
+import { ContextMenuProvider, useDialog } from "@finos/vuu-popups";
import {
- Feature,
- SessionEditingForm,
+ LeftNav,
Shell,
ShellContextProvider,
ShellProps,
- ThemeProvider,
VuuUser,
} from "@finos/vuu-shell";
-import { ReactElement, useCallback, useRef, useState } from "react";
-import { AppSidePanel } from "./app-sidepanel";
import { getDefaultColumnConfig } from "./columnMetaData";
-import { getFormConfig } from "./session-editing";
import { createPlaceholder } from "./createPlaceholder";
+import { useFeatures } from "./useFeatures";
+import {
+ ColumnSettingsPanel,
+ TableSettingsPanel,
+} from "@finos/vuu-table-extras";
+import {
+ registerComponent,
+ useLayoutContextMenuItems,
+} from "@finos/vuu-layout";
import "./App.css";
-// Because we do not render the AppSidePanel directly, the css will not be included in bundle.
-import "./app-sidepanel/AppSidePanel.css";
-import { VuuTable } from "@finos/vuu-protocol-types";
-
-const defaultWebsocketUrl = `wss://${location.hostname}:8090/websocket`;
-const { websocketUrl: serverUrl = defaultWebsocketUrl, features } =
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- await vuuConfig;
+import { useRpcResponseHandler } from "./useRpcResponseHandler";
-//TODO how do we separate this from the feature
-const vuuBlotterUrl = "./feature-vuu-table/index.js";
-// const vuuBlotterUrl = "./feature-vuu-table/index.js";
+registerComponent("ColumnSettings", ColumnSettingsPanel, "view");
+registerComponent("TableSettings", TableSettingsPanel, "view");
// createNewChild is used when we add a new Tab to Stack
const layoutProps: ShellProps["LayoutProps"] = {
@@ -37,83 +30,52 @@ const layoutProps: ShellProps["LayoutProps"] = {
pathToDropTarget: "#main-tabs.ACTIVE_CHILD",
};
-const withTable = (action: unknown): action is { table: VuuTable } =>
- action !== null && typeof action === "object" && "table" in action;
+const defaultWebsocketUrl = `wss://${location.hostname}:8090/websocket`;
+const {
+ websocketUrl: serverUrl = defaultWebsocketUrl,
+ features: configuredFeatures,
+} =
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ await vuuConfig;
export const App = ({ user }: { user: VuuUser }) => {
- const dialogTitleRef = useRef("");
- const [dialogContent, setDialogContent] = useState();
- const handleClose = () => {
- setDialogContent(undefined);
- };
+ const [features, tableFeatures] = useFeatures({
+ features: configuredFeatures,
+ });
- const tables = useVuuTables();
-
- const handleRpcResponse: RpcResponseHandler = useCallback(
- (response) => {
- if (
- hasAction(response) &&
- typeof response.action === "object" &&
- response.action !== null &&
- "type" in response.action &&
- response.action?.type === "OPEN_DIALOG_ACTION"
- ) {
- const { tableSchema } = response.action as unknown as {
- tableSchema: TableSchema;
- };
- if (tableSchema) {
- const formConfig = getFormConfig(response as MenuRpcResponse);
- dialogTitleRef.current = formConfig.config.title;
- setDialogContent(
-
- );
- } else if (
- withTable(response.action) &&
- tables &&
- response.action.table
- ) {
- const schema = tables.get(response.action.table.table);
- if (schema) {
- // If we already have this table open in this viewport, ignore
- setDialogContent(
-
- );
- }
- }
- } else {
- console.warn(`App, handleServiceRequest ${JSON.stringify(response)}`);
- }
- },
- [tables]
- );
+ const { dialog, setDialogState } = useDialog();
+ const { handleRpcResponse } = useRpcResponseHandler(setDialogState);
+ const { buildMenuOptions, handleMenuAction } =
+ useLayoutContextMenuItems(setDialogState);
// TODO get Context from Shell
return (
-
-
+
+
}
+ leftSidePanelLayout="full-height"
+ leftSidePanel={
+
+ }
+ saveUrl="https://localhost:8443/api/vui"
serverUrl={serverUrl}
user={user}
>
-
+ {dialog}
-
-
+
+
);
};
diff --git a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.css b/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.css
deleted file mode 100644
index c6028d563..000000000
--- a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.css
+++ /dev/null
@@ -1,5 +0,0 @@
-
-.vuuFeatureDropdown {
- border-bottom: solid 1px var(--salt-container-primary-borderColor);
- margin-bottom: var(--salt-space-unit);
-}
\ No newline at end of file
diff --git a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.tsx b/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.tsx
deleted file mode 100644
index 32e884ba2..000000000
--- a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-import { byModule, TableSchema } from "@finos/vuu-data";
-import { Palette, PaletteItem, ViewProps } from "@finos/vuu-layout";
-import { Feature, Features } from "@finos/vuu-shell";
-import {
- Accordion,
- AccordionGroup,
- AccordionHeader,
- AccordionPanel,
-} from "@salt-ds/core";
-import { Dropdown, SelectionChangeHandler } from "@salt-ds/lab";
-import cx from "classnames";
-import { ReactElement, useMemo, useState } from "react";
-
-import "./AppSidePanel.css";
-
-const NO_FEATURES: Features = {};
-const NULL_FEATURE = {};
-export interface AppSidePanelProps {
- features?: Features;
- tables?: Map;
- ViewProps?: Partial;
-}
-
-type FeatureDescriptor = {
- className: string;
- css: string;
- js: string;
- name: string;
- title: string;
-};
-
-const capitalize = (text: string) =>
- text.length === 0 ? "" : text[0].toUpperCase() + text.slice(1);
-
-const regexp_worfify = /(? {
- const [firstWord, ...rest] = text.split(regexp_worfify);
- return `${capitalize(firstWord)} ${rest.join(" ")}`;
-};
-
-const classBase = "vuuAppSidePanel";
-
-export const AppSidePanel = ({
- features = NO_FEATURES,
- tables,
- ViewProps,
-}: AppSidePanelProps) => {
- const gridFeatures = useMemo(
- () =>
- Object.entries(features).map(([featureName, { title, url, css }]) => {
- return {
- className: featureName,
- css,
- js: url,
- name: featureName,
- title,
- } as FeatureDescriptor;
- }),
- [features]
- );
-
- const [selectedFeature, setSelectedFeature] = useState(
- gridFeatures[0] ?? NULL_FEATURE
- );
- const handleSelectFeature: SelectionChangeHandler = (event, item) => {
- const feature = gridFeatures.find((f) => f.title === item);
- if (feature) {
- setSelectedFeature(feature);
- }
- };
-
- const paletteItems = useMemo(() => {
- return tables === undefined
- ? []
- : Array.from(tables.values())
- .sort(byModule)
- .map((schema) => {
- const { className, css, js } = selectedFeature;
- return {
- component: (
-
- ),
- id: schema.table.table,
- label: `${schema.table.module} ${wordify(schema.table.table)}`,
- };
- });
- }, [selectedFeature, tables]);
-
- const featureSelection = (): ReactElement => {
- const featureNames = gridFeatures.map((f) => f.title);
- if (featureNames.length === 1) {
- return {featureNames[0]}
;
- } else {
- return (
-
- className="vuuFeatureDropdown"
- fullWidth
- onSelectionChange={handleSelectFeature}
- selected={selectedFeature?.title}
- source={featureNames}
- />
- );
- }
- };
-
- return (
-
-
-
- My Layouts
-
-
-
- Vuu Tables
-
- <>
- {featureSelection()}
-
- {paletteItems.map((spec) => (
-
- {spec.component}
-
- ))}
-
- >
-
-
-
- Layout Templates
-
-
-
-
- );
-};
diff --git a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/index.ts b/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/index.ts
deleted file mode 100644
index fa3d1a25a..000000000
--- a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./AppSidePanel";
diff --git a/vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts b/vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts
index 7a8016780..9fe83b886 100644
--- a/vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts
+++ b/vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts
@@ -57,7 +57,7 @@ const columnMetaData: { [key: string]: Partial } = {
label: "Ask",
type: {
name: "number",
- renderer: { name: "background", flashStyle: "arrow-bg" },
+ renderer: { name: "vuu.price-move-background", flashStyle: "arrow-bg" },
formatting: { decimals: 2, zeroPad: true },
},
aggregate: Average,
@@ -90,7 +90,7 @@ const columnMetaData: { [key: string]: Partial } = {
name: "bid",
type: {
name: "number",
- renderer: { name: "background", flashStyle: "arrow-bg" },
+ renderer: { name: "vuu.price-move-background", flashStyle: "arrow-bg" },
formatting: { decimals: 2, zeroPad: true },
},
aggregate: Average,
@@ -100,6 +100,8 @@ const columnMetaData: { [key: string]: Partial } = {
name: "bidSize",
type: {
name: "number",
+ renderer: { name: "vuu.price-move-background", flashStyle: "bg-only" },
+ formatting: { decimals: 2, zeroPad: true },
},
aggregate: Average,
},
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/useFeatures.ts b/vuu-ui/sample-apps/app-vuu-example/src/useFeatures.ts
similarity index 100%
rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/useFeatures.ts
rename to vuu-ui/sample-apps/app-vuu-example/src/useFeatures.ts
diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/useRpcResponseHandler.tsx b/vuu-ui/sample-apps/app-vuu-example/src/useRpcResponseHandler.tsx
similarity index 97%
rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/useRpcResponseHandler.tsx
rename to vuu-ui/sample-apps/app-vuu-example/src/useRpcResponseHandler.tsx
index f21679dcd..15b6292b0 100644
--- a/vuu-ui/sample-apps/app-vuu-basket-trader/src/useRpcResponseHandler.tsx
+++ b/vuu-ui/sample-apps/app-vuu-example/src/useRpcResponseHandler.tsx
@@ -2,7 +2,7 @@ import { RpcResponseHandler, useVuuTables } from "@finos/vuu-data-react";
import { hasAction, MenuRpcResponse, TableSchema } from "@finos/vuu-data";
import { useCallback } from "react";
import { getFormConfig } from "./session-editing";
-import { Feature, SessionEditingForm } from "packages/vuu-shell/src";
+import { Feature, SessionEditingForm } from "@finos/vuu-shell";
import { VuuTable } from "@finos/vuu-protocol-types";
import { SetDialog } from "@finos/vuu-popups";
diff --git a/vuu-ui/sample-apps/feature-basket-trading/index.ts b/vuu-ui/sample-apps/feature-basket-trading/index.ts
index 683bbe19a..959fa2995 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/index.ts
+++ b/vuu-ui/sample-apps/feature-basket-trading/index.ts
@@ -6,3 +6,5 @@ export type { basketDataSourceKey } from "./src/useBasketTradingDatasources";
export { BasketSelector } from "./src/basket-selector";
export { BasketToolbar } from "./src/basket-toolbar";
export { NewBasketPanel } from "./src/new-basket-panel";
+
+export { type Basket } from "./src/useBasketTrading";
diff --git a/vuu-ui/sample-apps/feature-basket-trading/package.json b/vuu-ui/sample-apps/feature-basket-trading/package.json
index e8c343ac7..1048ffa0d 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/package.json
+++ b/vuu-ui/sample-apps/feature-basket-trading/package.json
@@ -5,7 +5,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "node ../../scripts/build-feature.mjs",
- "start": "serve -p 5002 ../../deployed_apps/app-vuu-basket-trader"
+ "start": "serve -p 5002 ../../deployed_apps/app-vuu-example"
},
"private": true,
"keywords": [],
@@ -53,7 +53,7 @@
},
{
"module": "BASKET",
- "table": "basketTradingConstituent"
+ "table": "basketTradingConstituentJoin"
},
{
"module": "SIMUL",
diff --git a/vuu-ui/sample-apps/feature-basket-trading/src/VuuBasketTradingFeature.tsx b/vuu-ui/sample-apps/feature-basket-trading/src/VuuBasketTradingFeature.tsx
index 5e69cf41c..d40c26a70 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/src/VuuBasketTradingFeature.tsx
+++ b/vuu-ui/sample-apps/feature-basket-trading/src/VuuBasketTradingFeature.tsx
@@ -1,13 +1,9 @@
import { TableSchema } from "@finos/vuu-data";
import { FlexboxLayout, Stack } from "@finos/vuu-layout";
import { ContextMenuProvider } from "@finos/vuu-popups";
-import { useEffect, useMemo, useState } from "react";
-import { BasketSelectorProps } from "./basket-selector";
import { BasketTableEdit } from "./basket-table-edit";
import { BasketTableLive } from "./basket-table-live";
import { BasketToolbar } from "./basket-toolbar";
-import { useBasketTabMenu } from "./useBasketTabMenu";
-import { useBasketTradingDataSources } from "./useBasketTradingDatasources";
import "./VuuBasketTradingFeature.css";
import { EmptyBasketsPanel } from "./empty-baskets-panel";
@@ -21,7 +17,7 @@ const basketStatus: [BasketStatus, BasketStatus] = ["design", "on-market"];
export interface BasketTradingFeatureProps {
basketSchema: TableSchema;
basketTradingSchema: TableSchema;
- basketTradingConstituentSchema: TableSchema;
+ basketTradingConstituentJoinSchema: TableSchema;
instrumentsSchema: TableSchema;
}
@@ -29,111 +25,52 @@ const VuuBasketTradingFeature = (props: BasketTradingFeatureProps) => {
const {
basketSchema,
basketTradingSchema,
- basketTradingConstituentSchema,
+ basketTradingConstituentJoinSchema,
instrumentsSchema,
} = props;
- const basketInstanceId = "steve-00001";
-
const {
activeTabIndex,
- dataSourceBasket,
- dataSourceBasketTrading,
- dataSourceBasketTradingControl,
- dataSourceBasketTradingSearch,
- dataSourceBasketTradingConstituent,
- dataSourceInstruments,
+ basket,
+ basketCount,
+ basketSelectorProps,
+ contextMenuProps,
+ dataSourceBasketTradingConstituentJoin,
+ dialog,
+ onClickAddBasket,
+ onCommitBasketChange,
onSendToMarket,
onTakeOffMarket,
- } = useBasketTradingDataSources({
- basketInstanceId,
+ } = useBasketTrading({
basketSchema,
basketTradingSchema,
- basketTradingConstituentSchema,
+ basketTradingConstituentJoinSchema,
instrumentsSchema,
});
- const [basketCount, setBasketCount] = useState(-1);
- useMemo(() => {
- dataSourceBasketTradingControl.subscribe(
- {
- range: { from: 0, to: 100 },
- },
- (message) => {
- console.log("message from dataSourceTradingControl", {
- message,
- });
- if (message.size) {
- setBasketCount(message.size);
- }
- if (message.rows) {
- console.table(message.rows);
- }
- }
- );
-
- // TEMP server is notsending TABLE_ROWS if size is zero
- setTimeout(() => {
- setBasketCount((count) => (count === -1 ? 0 : count));
- }, 1000);
- }, [dataSourceBasketTradingControl]);
- useEffect(() => {
- // dataSourceBasketDesign.resume?.();
- return () => {
- dataSourceBasketTradingControl.unsubscribe?.();
- };
- }, [dataSourceBasketTradingControl]);
-
- const [buildMenuOptions, handleMenuAction] = useBasketTabMenu({
- dataSourceInstruments,
- });
-
- const { dialog, handleAddBasket } = useBasketTrading({
- basketSchema,
- dataSourceBasket,
- });
-
- // useMemo(() => {
- // dataSourceBasketTrading.filter = {
- // filter: `basketId = "${basketId}"`,
- // };
- // }, [basketId, dataSourceBasketTrading]);
-
- const basketSelectorProps = useMemo(
- () => ({
- basketInstanceId,
- dataSourceBasketTrading,
- dataSourceBasketTradingSearch: dataSourceBasketTradingSearch,
- onClickAddBasket: handleAddBasket,
- }),
- [dataSourceBasketTrading, dataSourceBasketTradingSearch, handleAddBasket]
- );
-
if (basketCount === -1) {
// TODO loading
return null;
} else if (basketCount === 0) {
return (
<>
-
+
{dialog}
>
);
}
return (
-
+
@@ -146,13 +83,13 @@ const VuuBasketTradingFeature = (props: BasketTradingFeatureProps) => {
diff --git a/vuu-ui/sample-apps/feature-basket-trading/src/basket-selector/BasketSelector.tsx b/vuu-ui/sample-apps/feature-basket-trading/src/basket-selector/BasketSelector.tsx
index 81b5381a3..15bc914ec 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/src/basket-selector/BasketSelector.tsx
+++ b/vuu-ui/sample-apps/feature-basket-trading/src/basket-selector/BasketSelector.tsx
@@ -3,47 +3,48 @@ import { Button } from "@salt-ds/core";
import { DataSource } from "@finos/vuu-data";
import { useId } from "@finos/vuu-layout";
import { DropdownBaseProps, InstrumentSearch } from "@finos/vuu-ui-controls";
-import { HTMLAttributes, useCallback, useRef } from "react";
+import { HTMLAttributes, useRef } from "react";
import "./BasketSelector.css";
import { useBasketSelector } from "./useBasketSelector";
+import { Basket } from "../useBasketTrading";
const classBase = "vuuBasketSelector";
export interface BasketSelectorProps
extends Pick,
HTMLAttributes {
+ basket?: Basket;
basketInstanceId?: string;
- dataSourceBasketTrading: DataSource;
dataSourceBasketTradingSearch: DataSource;
label?: string;
onClickAddBasket: () => void;
+ onSelectBasket: (basketInstanceId: string) => void;
}
export const BasketSelector = ({
+ basket,
basketInstanceId,
- dataSourceBasketTrading,
dataSourceBasketTradingSearch,
id: idProp,
isOpen: isOpenProp,
- onClickAddBasket,
+ onClickAddBasket: onClickAddBasketProp,
onOpenChange: onOpenChangeProp,
+ onSelectBasket,
...htmlAttributes
}: BasketSelectorProps) => {
const rootRef = useRef(null);
const id = useId(idProp);
- const { basket, isOpen, onOpenChange, tableProps } = useBasketSelector({
- basketInstanceId,
- dataSourceBasketTrading,
- dataSourceBasketTradingSearch,
- isOpen: isOpenProp,
- onOpenChange: onOpenChangeProp,
- });
-
- const handleClickAddBasket = useCallback(() => {
- onClickAddBasket();
- }, [onClickAddBasket]);
+ const { isOpen, onClickAddBasket, onOpenChange, tableProps } =
+ useBasketSelector({
+ basketInstanceId,
+ dataSourceBasketTradingSearch,
+ isOpen: isOpenProp,
+ onClickAddBasket: onClickAddBasketProp,
+ onOpenChange: onOpenChangeProp,
+ onSelectBasket,
+ });
return (
-
diff --git a/vuu-ui/sample-apps/feature-basket-trading/src/basket-selector/useBasketSelector.ts b/vuu-ui/sample-apps/feature-basket-trading/src/basket-selector/useBasketSelector.ts
index e1734e666..805f8569d 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/src/basket-selector/useBasketSelector.ts
+++ b/vuu-ui/sample-apps/feature-basket-trading/src/basket-selector/useBasketSelector.ts
@@ -1,41 +1,29 @@
import { TableProps, TableRowClickHandler } from "@finos/vuu-table";
-import { buildColumnMap, ColumnMap } from "@finos/vuu-utils";
-import { SubscribeCallback } from "@finos/vuu-data";
-import { VuuDataRow } from "@finos/vuu-protocol-types";
+import { buildColumnMap } from "@finos/vuu-utils";
import { OpenChangeHandler, useControlled } from "@finos/vuu-ui-controls";
-import { useCallback, useMemo, useRef, useState } from "react";
+import { useCallback, useMemo } from "react";
import { BasketSelectorProps } from "./BasketSelector";
import { BasketSelectorRow } from "./BasketSelectorRow";
-export class Basket {
- basketId: string;
- basketName: string;
- fxRateToUsd: number;
-
- constructor(data: VuuDataRow, columnMap: ColumnMap) {
- this.basketId = data[columnMap.basketId] as string;
- this.basketName = data[columnMap.basketName] as string;
- this.fxRateToUsd = data[columnMap.fxRateToUsd] as number;
- }
-}
-
export type BasketSelectorHookProps = Pick<
BasketSelectorProps,
| "basketInstanceId"
- | "dataSourceBasketTrading"
| "dataSourceBasketTradingSearch"
| "defaultIsOpen"
| "isOpen"
| "onOpenChange"
+ | "onClickAddBasket"
+ | "onSelectBasket"
>;
export const useBasketSelector = ({
- basketInstanceId: basketInstanceIdProp,
- dataSourceBasketTrading,
+ basketInstanceId,
dataSourceBasketTradingSearch,
defaultIsOpen,
isOpen: isOpenProp,
+ onClickAddBasket,
onOpenChange,
+ onSelectBasket,
}: BasketSelectorHookProps) => {
const [isOpen, setIsOpen] = useControlled({
controlled: isOpenProp,
@@ -43,14 +31,9 @@ export const useBasketSelector = ({
name: "useDropdownList",
});
- const [basketInstanceId, setBasketInstanceId] = useState(
- basketInstanceIdProp
- );
- const [basket, setBasket] = useState();
-
const columnMap = useMemo(
- () => buildColumnMap(dataSourceBasketTrading.columns),
- [dataSourceBasketTrading.columns]
+ () => buildColumnMap(dataSourceBasketTradingSearch.columns),
+ [dataSourceBasketTradingSearch.columns]
);
const handleOpenChange = useCallback(
@@ -58,7 +41,8 @@ export const useBasketSelector = ({
setIsOpen(open);
onOpenChange?.(open, closeReason);
if (open === false) {
- dataSourceBasketTradingSearch.unsubscribe();
+ console.log(`%cdisable basketSearch`, "color:red;font-weight:bold;");
+ dataSourceBasketTradingSearch.disable?.();
}
},
[dataSourceBasketTradingSearch, onOpenChange, setIsOpen]
@@ -66,34 +50,17 @@ export const useBasketSelector = ({
const handleRowClick = useCallback(
(row) => {
- const instanceIdId = row[columnMap.instanceId] as string;
- setBasketInstanceId(instanceIdId);
- setIsOpen(false);
- dataSourceBasketTrading.filter = {
- filter: `instanceId = "${basketInstanceId}"`,
- };
+ const instanceId = row[columnMap.instanceId] as string;
+ handleOpenChange(false, "select");
+ onSelectBasket?.(instanceId);
},
- [basketInstanceId, columnMap.instanceId, dataSourceBasketTrading, setIsOpen]
+ [columnMap.instanceId, handleOpenChange, onSelectBasket]
);
- const handleData = useCallback(
- (message) => {
- if (message.type === "viewport-update" && message.rows?.length === 1) {
- setBasket(new Basket(message.rows[0], columnMap));
- }
- },
- [columnMap]
- );
-
- useMemo(() => {
- dataSourceBasketTrading.subscribe(
- {
- range: { from: 0, to: 1 },
- filter: { filter: `instanceId = "${basketInstanceId}"` },
- },
- handleData
- );
- }, [dataSourceBasketTrading, basketInstanceId, handleData]);
+ const handleClickAddBasket = useCallback(() => {
+ handleOpenChange(false, "script");
+ onClickAddBasket();
+ }, [handleOpenChange, onClickAddBasket]);
const tableProps: Partial = useMemo(
() => ({
@@ -128,13 +95,14 @@ export const useBasketSelector = ({
},
onRowClick: handleRowClick,
rowHeight: 47,
+ selectedKeys: basketInstanceId ? [basketInstanceId] : undefined,
}),
- [handleRowClick]
+ [basketInstanceId, handleRowClick]
);
return {
- basket,
isOpen,
+ onClickAddBasket: handleClickAddBasket,
onOpenChange: handleOpenChange,
tableProps,
};
diff --git a/vuu-ui/sample-apps/feature-basket-trading/src/basket-table-edit/BasketTableEdit.tsx b/vuu-ui/sample-apps/feature-basket-trading/src/basket-table-edit/BasketTableEdit.tsx
index 1b3652a0f..d12b1b48b 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/src/basket-table-edit/BasketTableEdit.tsx
+++ b/vuu-ui/sample-apps/feature-basket-trading/src/basket-table-edit/BasketTableEdit.tsx
@@ -2,6 +2,7 @@ import { TableSchema } from "@finos/vuu-data";
import { ColumnDescriptor, TableConfig } from "@finos/vuu-datagrid-types";
import { TableNext, TableProps } from "@finos/vuu-table";
import { useMemo } from "react";
+import columns from "./basketConstituentEditColumns";
import "./BasketTableEdit.css";
@@ -23,27 +24,6 @@ const labels: { [key: string]: string } = {
const applyColumnDefaults = (tableSchema: TableSchema) =>
tableSchema.columns.map((column) => {
switch (column.name) {
- case "ric":
- return {
- ...column,
- label: "Ticker",
- pin: "left",
- };
- case "ask":
- case "bid":
- case "last":
- return {
- ...column,
- label: labels[column.name] ?? column.name,
- type: {
- name: "number",
- formatting: {
- alignOnDecimals: true,
- decimals: 2,
- zeroPad: true,
- },
- },
- } as ColumnDescriptor;
case "limitPrice":
return {
...column,
@@ -61,26 +41,6 @@ const applyColumnDefaults = (tableSchema: TableSchema) =>
},
},
} as ColumnDescriptor;
- case "priceStrategy":
- return {
- ...column,
- editable: true,
- label: labels[column.name] ?? column.name,
- type: {
- name: "string",
- renderer: {
- name: "dropdown-cell",
- // TODO how do we get these
- values: [
- "Peg to near touch",
- "Limit",
- "Strategy 3",
- "Strategy 4",
- "Strategy 5",
- ],
- },
- },
- };
case "quantity":
return {
...column,
@@ -119,20 +79,26 @@ const applyColumnDefaults = (tableSchema: TableSchema) =>
});
export const BasketTableEdit = ({
+ dataSource,
tableSchema,
...props
}: BasketTableEditProps) => {
+ useMemo(() => {
+ dataSource.columns = columns.map((col) => col.name);
+ }, [dataSource]);
+
const tableConfig = useMemo(
() => ({
- columns: applyColumnDefaults(tableSchema),
+ columns,
rowSeparators: true,
}),
- [tableSchema]
+ []
);
return (
CommitResponse;
export interface BasketToolbarProps extends HTMLAttributes {
+ basket?: Basket;
basketStatus: BasketStatus;
BasketSelectorProps: BasketSelectorProps;
- basketTradingDataSource: DataSource;
+ onCommit?: BasketChangeHandler;
onSendToMarket: () => void;
onTakeOffMarket: () => void;
}
export const BasketToolbar = ({
+ basket,
BasketSelectorProps,
basketStatus,
- basketTradingDataSource,
+ onCommit,
onSendToMarket,
onTakeOffMarket,
}: BasketToolbarProps) => {
@@ -31,42 +47,100 @@ export const BasketToolbar = ({
return true;
};
+ const handleUnitsEdited = useCallback(
+ (value) => {
+ if (onCommit) {
+ return onCommit?.("units", value);
+ } else {
+ throw Error(
+ "BasketToolbar onCommit prop not supplied for editable Basket"
+ );
+ }
+ },
+ [onCommit]
+ );
+
+ const { warningMessage: unitErrorMessage, ...unitProps } = useEditableText({
+ initialValue: basket?.units ?? "",
+ onCommit: handleUnitsEdited,
+ });
+
+ const handleSideCommit = useCallback(
+ (_, value) => {
+ if (onCommit) {
+ return onCommit?.("side", value);
+ } else {
+ throw Error(
+ "BasketToolbar onCommit prop not supplied for editable Basket"
+ );
+ }
+ },
+ [onCommit]
+ );
+
const basketSelector = (
-
+
);
const statusIndicator = (
);
+ const inputSide = (
+
+ Side
+
+
+ );
+ const readOnlySide = (
+
+ Units
+ {basket?.side ?? ""}
+
+ );
const inputUnits = (
Units
-
+
);
const readOnlyUnits = (
Units
- 100
+ {basket?.units ?? ""}
);
const notionalUSD = (
Total USD Not
- 1,235,789
+
+ {basket?.totalNotional ?? ""}
+
);
const notional = (
Total Not
- 2,345,678
+
+ {basket?.totalNotionalUsd ?? ""}
+
);
const pctFilled = (
% Filled
- 25%
+ {basket?.filledPct ?? ""}
);
@@ -98,10 +172,17 @@ export const BasketToolbar = ({
const getToolbarItems = () => {
const toolbarItems = [basketSelector];
if (basketStatus === "design") {
- toolbarItems.push(inputUnits, notionalUSD, notional, sendToMarket);
+ toolbarItems.push(
+ inputSide,
+ inputUnits,
+ notionalUSD,
+ notional,
+ sendToMarket
+ );
} else {
toolbarItems.push(
statusIndicator,
+ readOnlySide,
readOnlyUnits,
notionalUSD,
notional,
diff --git a/vuu-ui/sample-apps/feature-basket-trading/src/new-basket-panel/NewBasketPanel.tsx b/vuu-ui/sample-apps/feature-basket-trading/src/new-basket-panel/NewBasketPanel.tsx
index 496f63b96..ab6f57007 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/src/new-basket-panel/NewBasketPanel.tsx
+++ b/vuu-ui/sample-apps/feature-basket-trading/src/new-basket-panel/NewBasketPanel.tsx
@@ -11,7 +11,7 @@ import {
} from "@finos/vuu-ui-controls";
import { Button, FormField, FormFieldLabel } from "@salt-ds/core";
import cx from "classnames";
-import { DataSourceRow } from "packages/vuu-data-types";
+import { DataSourceRow } from "@finos/vuu-data-types";
import { HTMLAttributes, useMemo } from "react";
import "./NewBasketPanel.css";
@@ -42,7 +42,6 @@ export const NewBasketPanel = ({
columnMap,
onChangeBasketName,
onOpenChangeInstrumentPicker,
- onFeatureEnabled,
onSave,
onSelectBasket,
saveButtonDisabled,
@@ -65,9 +64,8 @@ export const NewBasketPanel = ({
rowSeparators: true,
},
dataSource: basketDataSource,
- onFeatureEnabled,
}),
- [basketDataSource, onFeatureEnabled]
+ [basketDataSource]
);
const itemToString = displayName(columnMap.name);
diff --git a/vuu-ui/sample-apps/feature-basket-trading/src/new-basket-panel/useNewBasketPanel.ts b/vuu-ui/sample-apps/feature-basket-trading/src/new-basket-panel/useNewBasketPanel.ts
index c3aaa80c1..1f309f6ba 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/src/new-basket-panel/useNewBasketPanel.ts
+++ b/vuu-ui/sample-apps/feature-basket-trading/src/new-basket-panel/useNewBasketPanel.ts
@@ -1,5 +1,3 @@
-import { VuuFeatureMessage } from "@finos/vuu-data";
-import { isViewportMenusAction } from "@finos/vuu-data-react";
import {
ClientToServerMenuRPC,
VuuMenu,
@@ -8,7 +6,7 @@ import {
import { TableRowSelectHandler } from "@finos/vuu-table";
import { Commithandler, OpenChangeHandler } from "@finos/vuu-ui-controls";
import { buildColumnMap, metadataKeys } from "@finos/vuu-utils";
-import { useCallback, useRef, useState } from "react";
+import { useCallback, useState } from "react";
import { NewBasketPanelProps } from "./NewBasketPanel";
const { KEY } = metadataKeys;
@@ -42,7 +40,6 @@ export const useNewBasketPanel = ({
basketSchema,
onSaveBasket,
}: NewBasketHookProps) => {
- const rpcCommandRef = useRef();
const columnMap = buildColumnMap(basketSchema.columns);
const [basketName, setBasketName] = useState("");
const [basketId, setBasketId] = useState();
@@ -50,16 +47,26 @@ export const useNewBasketPanel = ({
const saveBasket = useCallback(() => {
if (basketName && basketId) {
onSaveBasket(basketName, basketId);
-
- if (rpcCommandRef.current) {
- basketDataSource.menuRpcCall({
- rpcName: rpcCommandRef.current,
- type: "VIEW_PORT_MENUS_SELECT_RPC",
- } as Omit);
-
- requestAnimationFrame(() => {
- basketDataSource.unsubscribe();
- });
+ if (basketDataSource?.menu) {
+ const rpcCommand = getRpcCommand(
+ basketDataSource?.menu?.menus,
+ "CREATE_NEW_BASKET"
+ );
+ if (rpcCommand) {
+ basketDataSource
+ .menuRpcCall({
+ // basketName: basketName,
+ rpcName: rpcCommand.rpcName,
+ type: "VIEW_PORT_MENUS_SELECT_RPC",
+ } as Omit)
+ .then((response) => {
+ console.log(`rpmMenuResponse`, { response });
+ });
+ }
+ } else {
+ throw Error(
+ "useNewBasketPanel cannot create basket, datasource has no menu"
+ );
}
}
}, [basketDataSource, basketId, basketName, onSaveBasket]);
@@ -80,31 +87,19 @@ export const useNewBasketPanel = ({
[]
);
- const handleFeatureEnabled = useCallback((message: VuuFeatureMessage) => {
- if (isViewportMenusAction(message)) {
- const {
- menu: { menus },
- } = message;
- const rpcCommand = getRpcCommand(menus, "CREATE_NEW_BASKET");
- console.log({ rpcCommand });
- rpcCommandRef.current = rpcCommand?.rpcName;
- }
- }, []);
-
const handleOpenChangeInstrumentPicker = useCallback(
- (open, closeReason) => {
+ (open) => {
if (!open) {
- console.log(`instrument picker closed ${closeReason}`);
+ basketDataSource.disable?.();
}
},
- []
+ [basketDataSource]
);
return {
columnMap,
onChangeBasketName: handleChangeBasketName,
onCloseInstrumentPicker: handleOpenChangeInstrumentPicker,
- onFeatureEnabled: handleFeatureEnabled,
onOpenChangeInstrumentPicker: handleOpenChangeInstrumentPicker,
onSave: saveBasket,
onSelectBasket: handleSelectBasket,
diff --git a/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTrading.tsx b/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTrading.tsx
index 90d2f5992..8d3245b52 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTrading.tsx
+++ b/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTrading.tsx
@@ -1,15 +1,48 @@
-import { DataSource, TableSchema } from "@finos/vuu-data";
import { useViewContext } from "@finos/vuu-layout";
-import { useCallback, useMemo, useState } from "react";
+import { buildColumnMap, ColumnMap } from "@finos/vuu-utils";
+import { DataSourceRow } from "packages/vuu-data-types";
+import { useCallback, useEffect, useMemo, useState } from "react";
+import { BasketSelectorProps } from "./basket-selector";
+import { BasketChangeHandler } from "./basket-toolbar";
import { NewBasketPanel } from "./new-basket-panel";
+import { useBasketTabMenu } from "./useBasketTabMenu";
+import { useBasketTradingDataSources } from "./useBasketTradingDatasources";
+import { BasketTradingFeatureProps } from "./VuuBasketTradingFeature";
-export interface BasketTradingHookProsp {
- basketSchema: TableSchema;
- dataSourceBasket: DataSource;
+export class Basket {
+ basketId: string;
+ basketName: string;
+ dataSourceRow: DataSourceRow;
+ filledPct: number;
+ fxRateToUsd: number;
+ side: string;
+ totalNotional: number;
+ totalNotionalUsd: number;
+ units: number;
+
+ constructor(data: DataSourceRow, columnMap: ColumnMap) {
+ this.dataSourceRow = data;
+ this.basketId = data[columnMap.basketId] as string;
+ this.basketName = data[columnMap.basketName] as string;
+ this.filledPct = data[columnMap.filledPct] as number;
+ this.fxRateToUsd = data[columnMap.fxRateToUsd] as number;
+ this.side = "BUY";
+ this.totalNotional = data[columnMap.totalNotional] as number;
+ this.totalNotionalUsd = data[columnMap.totalNotionalUsd] as number;
+ this.units = data[columnMap.units] as number;
+ }
}
+export type BasketTradingHookProps = Pick<
+ BasketTradingFeatureProps,
+ | "basketSchema"
+ | "basketTradingSchema"
+ | "basketTradingConstituentJoinSchema"
+ | "instrumentsSchema"
+>;
+
type BasketState = {
- basketId?: string;
+ basketInstanceId?: string;
dialog?: JSX.Element;
};
@@ -17,24 +50,79 @@ const NO_STATE = { basketId: undefined } as any;
export const useBasketTrading = ({
basketSchema,
- dataSourceBasket,
-}: BasketTradingHookProsp) => {
+ basketTradingSchema,
+ basketTradingConstituentJoinSchema,
+ instrumentsSchema,
+}: BasketTradingHookProps) => {
const { load, save } = useViewContext();
- const { basketId } = useMemo(() => {
- const { basketId } = load?.("basket-state") ?? NO_STATE;
- if (basketId) {
- console.log(`loaded basketId ${basketId}`);
- }
- return { basketId };
+ const basketInstanceId = useMemo(() => {
+ const { basketInstanceId } = load?.("basket-state") ?? NO_STATE;
+ return basketInstanceId;
}, [load]);
+ const {
+ activeTabIndex,
+ dataSourceBasket,
+ dataSourceBasketTradingControl,
+ dataSourceBasketTradingSearch,
+ dataSourceBasketTradingConstituentJoin,
+ dataSourceInstruments,
+ onSendToMarket,
+ onTakeOffMarket,
+ } = useBasketTradingDataSources({
+ basketInstanceId,
+ basketSchema,
+ basketTradingSchema,
+ basketTradingConstituentJoinSchema,
+ instrumentsSchema,
+ });
+
+ const [basket, setBasket] = useState();
+
+ const [basketCount, setBasketCount] = useState(-1);
+
const [basketState, setBasketState] = useState({
- basketId,
+ basketInstanceId,
dialog: undefined,
});
- const handleClose = useCallback(() => {
+ const columnMap = useMemo(
+ () => buildColumnMap(dataSourceBasketTradingControl.columns),
+ [dataSourceBasketTradingControl.columns]
+ );
+
+ useMemo(() => {
+ dataSourceBasketTradingControl.subscribe(
+ {
+ range: { from: 0, to: 1 },
+ },
+ (message) => {
+ if (message.type === "viewport-update") {
+ if (message.size) {
+ setBasketCount(message.size);
+ }
+ if (message.rows && message.rows.length > 0) {
+ setBasket(new Basket(message.rows[0], columnMap));
+ }
+ }
+ }
+ );
+
+ // TEMP server is notsending TABLE_ROWS if size is zero
+ setTimeout(() => {
+ setBasketCount((count) => (count === -1 ? 0 : count));
+ }, 800);
+ }, [columnMap, dataSourceBasketTradingControl]);
+
+ useEffect(() => {
+ return () => {
+ console.log("unsubscribe from dataSourceBasketTradingControl");
+ dataSourceBasketTradingControl.unsubscribe?.();
+ };
+ }, [dataSourceBasketTradingControl]);
+
+ const handleCloseNewBasketPanel = useCallback(() => {
setBasketState((state) => ({
...state,
dialog: undefined,
@@ -42,12 +130,26 @@ export const useBasketTrading = ({
}, []);
const handleSaveNewBasket = useCallback((basketName, basketId) => {
- setBasketState({
- basketId,
+ setBasketState((state) => ({
+ ...state,
dialog: undefined,
- });
+ }));
}, []);
+ const handleSelectBasket = useCallback(
+ (basketInstanceId: string) => {
+ save?.({ basketInstanceId }, "basket-state");
+ const filter = { filter: `instanceId = "${basketInstanceId}"` };
+ dataSourceBasketTradingConstituentJoin.filter = filter;
+ dataSourceBasketTradingControl.filter = filter;
+ },
+ [
+ dataSourceBasketTradingConstituentJoin,
+ dataSourceBasketTradingControl,
+ save,
+ ]
+ );
+
const handleAddBasket = useCallback(() => {
setBasketState((state) => ({
...state,
@@ -55,15 +157,68 @@ export const useBasketTrading = ({
),
}));
- }, [basketSchema, dataSourceBasket, handleClose, handleSaveNewBasket]);
+ }, [
+ basketSchema,
+ dataSourceBasket,
+ handleCloseNewBasketPanel,
+ handleSaveNewBasket,
+ ]);
+
+ const basketSelectorProps = useMemo>(
+ () => ({
+ basketInstanceId,
+ dataSourceBasketTradingSearch,
+ onClickAddBasket: handleAddBasket,
+ onSelectBasket: handleSelectBasket,
+ }),
+ [
+ basketInstanceId,
+ dataSourceBasketTradingSearch,
+ handleAddBasket,
+ handleSelectBasket,
+ ]
+ );
+
+ const handleCommitBasketChange = useCallback(
+ (columnName, value) => {
+ if (basket) {
+ const { dataSourceRow } = basket;
+ return dataSourceBasketTradingControl.applyEdit(
+ dataSourceRow,
+ columnName,
+ value
+ );
+ }
+ return Promise.resolve(true);
+ },
+ [basket, dataSourceBasketTradingControl]
+ );
+
+ const [menuBuilder, menuActionHandler] = useBasketTabMenu({
+ dataSourceInstruments,
+ });
+
+ const contextMenuProps = {
+ menuActionHandler,
+ menuBuilder,
+ };
return {
...basketState,
- handleAddBasket,
+ activeTabIndex,
+ basket,
+ basketCount,
+ basketSelectorProps,
+ contextMenuProps,
+ dataSourceBasketTradingConstituentJoin,
+ onClickAddBasket: handleAddBasket,
+ onCommitBasketChange: handleCommitBasketChange,
+ onSendToMarket,
+ onTakeOffMarket,
};
};
diff --git a/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTradingDatasources.ts b/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTradingDatasources.ts
index 0b739d355..480487423 100644
--- a/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTradingDatasources.ts
+++ b/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTradingDatasources.ts
@@ -2,21 +2,22 @@ import { useViewContext } from "@finos/vuu-layout";
import { DataSource, RemoteDataSource, TableSchema } from "@finos/vuu-data";
import { useCallback, useMemo, useState } from "react";
import { BasketTradingFeatureProps } from "./VuuBasketTradingFeature";
-import { VuuFilter } from "packages/vuu-protocol-types";
+import { VuuFilter } from "@finos/vuu-protocol-types";
export type basketDataSourceKey =
| "data-source-basket"
- | "data-source-basket-trading"
| "data-source-basket-trading-control"
| "data-source-basket-trading-search"
- | "data-source-basket-trading-constituent"
+ | "data-source-basket-trading-constituent-join"
| "data-source-instruments";
+const NO_FILTER = { filter: "" };
+
export const useBasketTradingDataSources = ({
basketSchema,
basketInstanceId,
basketTradingSchema,
- basketTradingConstituentSchema,
+ basketTradingConstituentJoinSchema,
instrumentsSchema,
}: BasketTradingFeatureProps & { basketInstanceId: string }) => {
const [activeTabIndex, setActiveTabIndex] = useState(0);
@@ -25,35 +26,45 @@ export const useBasketTradingDataSources = ({
const [
dataSourceBasket,
- dataSourceBasketTrading,
dataSourceBasketTradingControl,
dataSourceBasketTradingSearch,
- dataSourceBasketTradingConstituent,
+ dataSourceBasketTradingConstituentJoin,
dataSourceInstruments,
] = useMemo(() => {
- const basketFilter: VuuFilter = {
- filter: `instanceId = "${basketInstanceId}"`,
- };
- const dataSourceConfig: [basketDataSourceKey, TableSchema, VuuFilter?][] = [
- ["data-source-basket", basketSchema],
- ["data-source-basket-trading", basketTradingSchema, basketFilter],
- ["data-source-basket-trading-control", basketTradingSchema],
- ["data-source-basket-trading-search", basketTradingSchema, basketFilter],
+ const basketFilter: VuuFilter = basketInstanceId
+ ? {
+ filter: `instanceId = "${basketInstanceId}"`,
+ }
+ : NO_FILTER;
+ const dataSourceConfig: [
+ basketDataSourceKey,
+ TableSchema,
+ number,
+ VuuFilter?
+ ][] = [
+ ["data-source-basket", basketSchema, 100],
+ [
+ "data-source-basket-trading-control",
+ basketTradingSchema,
+ 0,
+ basketFilter,
+ ],
+ ["data-source-basket-trading-search", basketTradingSchema, 100],
[
- "data-source-basket-trading-constituent",
- basketTradingConstituentSchema,
+ "data-source-basket-trading-constituent-join",
+ basketTradingConstituentJoinSchema,
+ 100,
basketFilter,
],
- ["data-source-instruments", instrumentsSchema],
+ ["data-source-instruments", instrumentsSchema, 100],
];
const dataSources: DataSource[] = [];
- for (const [key, schema, filter] of dataSourceConfig) {
- console.log(`filter for ${key} = ${JSON.stringify(filter)}`);
+ for (const [key, schema, bufferSize, filter] of dataSourceConfig) {
let dataSource = loadSession?.(key) as RemoteDataSource;
if (dataSource === undefined) {
dataSource = new RemoteDataSource({
- bufferSize: 100,
+ bufferSize,
filter,
viewport: `${id}-${key}`,
table: schema.table,
@@ -69,7 +80,7 @@ export const useBasketTradingDataSources = ({
basketSchema,
basketTradingSchema,
basketInstanceId,
- basketTradingConstituentSchema,
+ basketTradingConstituentJoinSchema,
instrumentsSchema,
loadSession,
id,
@@ -88,10 +99,9 @@ export const useBasketTradingDataSources = ({
return {
activeTabIndex,
dataSourceBasket,
- dataSourceBasketTrading,
dataSourceBasketTradingControl,
dataSourceBasketTradingSearch,
- dataSourceBasketTradingConstituent,
+ dataSourceBasketTradingConstituentJoin,
dataSourceInstruments,
onSendToMarket: handleSendToMarket,
onTakeOffMarket: handleTakeOffMarket,
diff --git a/vuu-ui/sample-apps/feature-filter-table/package.json b/vuu-ui/sample-apps/feature-filter-table/package.json
index b60496af6..fc3226622 100644
--- a/vuu-ui/sample-apps/feature-filter-table/package.json
+++ b/vuu-ui/sample-apps/feature-filter-table/package.json
@@ -5,7 +5,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "node ../../scripts/build-feature.mjs",
- "start": "serve -p 5002 ../../deployed_apps/app-vuu-basket-trader"
+ "start": "serve -p 5002 ../../deployed_apps/app-vuu-example"
},
"private": true,
"keywords": [],
diff --git a/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx b/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx
index 262d3375e..5c7a039be 100644
--- a/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx
+++ b/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx
@@ -1,27 +1,20 @@
import {
DataSourceVisualLinkCreatedMessage,
SchemaColumn,
- TableSchema,
VuuFeatureInvocationMessage,
- VuuFeatureMessage,
} from "@finos/vuu-data";
-import {
- isViewportMenusAction,
- isVisualLinksAction,
- MenuActionConfig,
- useVuuMenuActions,
-} from "@finos/vuu-data-react";
+import { MenuActionConfig, useVuuMenuActions } from "@finos/vuu-data-react";
import { DataSourceFilter } from "@finos/vuu-data-types";
import { TableConfig } from "@finos/vuu-datagrid-types";
+import { Filter } from "@finos/vuu-filter-types";
import { FilterBarProps } from "@finos/vuu-filters";
import { ActiveItemChangeHandler, useViewContext } from "@finos/vuu-layout";
-import { LinkDescriptorWithLabel, VuuMenu } from "@finos/vuu-protocol-types";
-import { ShellContextProps, useShellContext } from "@finos/vuu-shell";
+import { useShellContext } from "@finos/vuu-shell";
+import { applyDefaultColumnConfig } from "@finos/vuu-utils";
import { Button } from "@salt-ds/core";
-import { Filter } from "packages/vuu-filter-types";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
-import { FilterTableFeatureProps } from "./VuuFilterTableFeature";
import { useSessionDataSource } from "./useSessionDataSource";
+import { FilterTableFeatureProps } from "./VuuFilterTableFeature";
const NO_CONFIG: FilterTableConfig = {};
@@ -30,27 +23,6 @@ const defaultTableConfig: Partial = {
zebraStripes: true,
};
-const applyDefaults = (
- { columns, table }: TableSchema,
- getDefaultColumnConfig?: ShellContextProps["getDefaultColumnConfig"]
-) => {
- if (typeof getDefaultColumnConfig === "function") {
- return columns.map((column) => {
- const config = getDefaultColumnConfig(table.table, column.name);
- if (config) {
- return {
- ...column,
- ...config,
- };
- } else {
- return column;
- }
- });
- } else {
- return columns;
- }
-};
-
type FilterTableConfig = {
"available-columns"?: SchemaColumn[];
"filterbar-config"?: Partial;
@@ -58,7 +30,7 @@ type FilterTableConfig = {
};
export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => {
- const { dispatch, load, save, loadSession, saveSession } = useViewContext();
+ const { dispatch, load, save } = useViewContext();
const {
"available-columns": availableColumnsFromState,
@@ -107,7 +79,6 @@ export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => {
const handleAvailableColumnsChange = useCallback(
(columns: SchemaColumn[]) => {
- console.log("save new available columns");
save?.(columns, "available-columns");
},
[save]
@@ -115,23 +86,11 @@ export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => {
const handleTableConfigChange = useCallback(
(config: TableConfig) => {
- console.log(`table config changed`);
save?.(config, "table-config");
},
[save]
);
- const handleVuuFeatureEnabled = useCallback(
- (message: VuuFeatureMessage) => {
- if (isViewportMenusAction(message)) {
- saveSession?.(message.menu, "vuu-menu");
- } else if (isVisualLinksAction(message)) {
- saveSession?.(message.links, "vuu-links");
- }
- },
- [saveSession]
- );
-
const handleVuuFeatureInvoked = useCallback(
(message: VuuFeatureInvocationMessage) => {
if (message.type === "vuu-link-created") {
@@ -164,7 +123,7 @@ export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => {
...tableConfigFromState,
columns:
tableConfigFromState?.columns ||
- applyDefaults(tableSchema, getDefaultColumnConfig),
+ applyDefaultColumnConfig(tableSchema, getDefaultColumnConfig),
}),
[getDefaultColumnConfig, tableConfigFromState, tableSchema]
);
@@ -187,7 +146,6 @@ export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => {
height: "auto",
onAvailableColumnsChange: handleAvailableColumnsChange,
onConfigChange: handleTableConfigChange,
- onFeatureEnabled: handleVuuFeatureEnabled,
onFeatureInvocation: handleVuuFeatureInvoked,
renderBufferSize: 50,
};
@@ -199,14 +157,8 @@ export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => {
get visualLink() {
return load?.("visual-link") as DataSourceVisualLinkCreatedMessage;
},
- get visualLinks() {
- return loadSession?.("vuu-links") as LinkDescriptorWithLabel[];
- },
- get vuuMenu() {
- return loadSession?.("vuu-menu") as VuuMenu;
- },
}),
- [load, loadSession]
+ [load]
);
useEffect(() => {
diff --git a/vuu-ui/sample-apps/feature-instrument-tiles/package.json b/vuu-ui/sample-apps/feature-instrument-tiles/package.json
index 8e095ee79..6227baecb 100644
--- a/vuu-ui/sample-apps/feature-instrument-tiles/package.json
+++ b/vuu-ui/sample-apps/feature-instrument-tiles/package.json
@@ -5,7 +5,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "node ../../scripts/build-feature.mjs",
- "start": "serve -p 5002 ../../deployed_apps/app-vuu-basket-trader"
+ "start": "serve -p 5002 ../../deployed_apps/app-vuu-example"
},
"private": true,
"keywords": [],
diff --git a/vuu-ui/sample-apps/feature-template/package.json b/vuu-ui/sample-apps/feature-template/package.json
index db724dca9..456fd94de 100644
--- a/vuu-ui/sample-apps/feature-template/package.json
+++ b/vuu-ui/sample-apps/feature-template/package.json
@@ -5,7 +5,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "node ../../scripts/build-feature.mjs",
- "start": "serve -p 5002 ../../deployed_apps/app-vuu-basket-trader"
+ "start": "serve -p 5002 ../../deployed_apps/app-vuu-example"
},
"private": true,
"keywords": [],
diff --git a/vuu-ui/sample-apps/feature-vuu-blotter/src/VuuBlotter.tsx b/vuu-ui/sample-apps/feature-vuu-blotter/src/VuuBlotter.tsx
index 02b08e50e..dbd16ab7a 100644
--- a/vuu-ui/sample-apps/feature-vuu-blotter/src/VuuBlotter.tsx
+++ b/vuu-ui/sample-apps/feature-vuu-blotter/src/VuuBlotter.tsx
@@ -1,17 +1,14 @@
import {
ConfigChangeMessage,
DataSourceVisualLinkCreatedMessage,
- RemoteDataSource,
- TableSchema,
-} from "@finos/vuu-data";
-import {
isViewportMenusAction,
isVisualLinkCreatedAction,
isVisualLinkRemovedAction,
isVisualLinksAction,
- MenuActionConfig,
- useVuuMenuActions,
-} from "@finos/vuu-data-react";
+ RemoteDataSource,
+ TableSchema,
+} from "@finos/vuu-data";
+import { MenuActionConfig, useVuuMenuActions } from "@finos/vuu-data-react";
import { Grid, GridProvider } from "@finos/vuu-datagrid";
import { GridAction, KeyedColumnDescriptor } from "@finos/vuu-datagrid-types";
import { Filter, FilterState } from "@finos/vuu-filter-types";
diff --git a/vuu-ui/sample-apps/feature-vuu-table/src/vuuTable.tsx b/vuu-ui/sample-apps/feature-vuu-table/src/vuuTable.tsx
index 8dc91c77e..de7da7910 100644
--- a/vuu-ui/sample-apps/feature-vuu-table/src/vuuTable.tsx
+++ b/vuu-ui/sample-apps/feature-vuu-table/src/vuuTable.tsx
@@ -5,14 +5,8 @@ import {
RemoteDataSource,
TableSchema,
VuuFeatureInvocationMessage,
- VuuFeatureMessage,
} from "@finos/vuu-data";
-import {
- isViewportMenusAction,
- isVisualLinksAction,
- MenuActionConfig,
- useVuuMenuActions,
-} from "@finos/vuu-data-react";
+import { MenuActionConfig, useVuuMenuActions } from "@finos/vuu-data-react";
import { GridConfig } from "@finos/vuu-datagrid-types";
import { Filter, FilterState } from "@finos/vuu-filter-types";
import {
@@ -162,17 +156,6 @@ const VuuTable = ({ schema, ...props }: FilteredTableProps) => {
dataSource.visualLink = undefined;
}, [dataSource]);
- const handleVuuFeatureEnabled = useCallback(
- (message: VuuFeatureMessage) => {
- if (isViewportMenusAction(message)) {
- saveSession?.(message.menu, "vuu-menu");
- } else if (isVisualLinksAction(message)) {
- saveSession?.(message.links, "vuu-links");
- }
- },
- [saveSession]
- );
-
const handleVuuFeatureInvoked = useCallback(
(message: VuuFeatureInvocationMessage) => {
if (message.type === "vuu-link-created") {
@@ -280,7 +263,6 @@ const VuuTable = ({ schema, ...props }: FilteredTableProps) => {
config={tableConfigRef.current}
dataSource={dataSource}
onConfigChange={handleTableConfigChange}
- onFeatureEnabled={handleVuuFeatureEnabled}
onFeatureInvocation={handleVuuFeatureInvoked}
renderBufferSize={100}
rowHeight={18}
diff --git a/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx b/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx
index e3a46a6f8..8a317b3ac 100644
--- a/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx
+++ b/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx
@@ -3,7 +3,7 @@ import {
registerComponent,
useLayoutContextMenuItems,
} from "@finos/vuu-layout";
-import { ContextMenuProvider, Dialog, useDialog } from "@finos/vuu-popups";
+import { ContextMenuProvider, useDialog } from "@finos/vuu-popups";
import {
FeatureConfig,
FeatureProps,
@@ -73,9 +73,8 @@ const features: FeatureProps[] = [
...featurePaths[env].BasketTrading,
ComponentProps: {
basketSchema: schemas.basket,
- // basketDefinitionsSchema: schemas.basketDefinitions,
- // basketDesignSchema: schemas.basketDesign,
- // basketOrdersSchema: schemas.basketOrders,
+ basketTradingSchema: schemas.basketTrading,
+ basketTradingConstituentJoinSchema: schemas.basketTradingConstituentJoin,
instrumentsSchema: schemas.instruments,
},
},
diff --git a/vuu-ui/showcase/src/examples/DataTable/FilterTable.examples.tsx b/vuu-ui/showcase/src/examples/DataTable/FilterTable.examples.tsx
index cce40f52f..fd71a4839 100644
--- a/vuu-ui/showcase/src/examples/DataTable/FilterTable.examples.tsx
+++ b/vuu-ui/showcase/src/examples/DataTable/FilterTable.examples.tsx
@@ -5,6 +5,7 @@ import { useCallback, useState } from "react";
import { useTableConfig, useTestDataSource } from "../utils";
import { DataSourceFilter } from "@finos/vuu-data-types";
import { getAllSchemas, getSchema } from "@finos/vuu-data-test";
+import { ActiveItemChangeHandler } from "packages/vuu-layout/src";
let displaySequence = 1;
@@ -34,10 +35,18 @@ export const DefaultFilterTable = () => {
[]
);
+ const handleChangeActiveFilterIndex = useCallback(
+ (index) => {
+ console.log(`active filters ${index.join(",")}`);
+ },
+ []
+ );
+
const filterBarProps = {
filters: [],
onApplyFilter: handleApplyFilter,
onChangeFilter: handleChangeFilter,
+ onChangeActiveFilterIndex: handleChangeActiveFilterIndex,
tableSchema,
};
@@ -83,9 +92,17 @@ export const FilterTableArrayDataInstruments = () => {
[dataSource]
);
+ const handleChangeActiveFilterIndex = useCallback(
+ (index) => {
+ console.log(`active filters ${index.join(",")}`);
+ },
+ []
+ );
+
const filterBarProps = {
filters: [],
onApplyFilter: handleApplyFilter,
+ onChangeActiveFilterIndex: handleChangeActiveFilterIndex,
tableSchema: getSchema("instruments"),
};
diff --git a/vuu-ui/showcase/src/examples/Filters/FilterBar/FilterBar.examples.tsx b/vuu-ui/showcase/src/examples/Filters/FilterBar/FilterBar.examples.tsx
index 3bd5f81a5..b48976fc0 100644
--- a/vuu-ui/showcase/src/examples/Filters/FilterBar/FilterBar.examples.tsx
+++ b/vuu-ui/showcase/src/examples/Filters/FilterBar/FilterBar.examples.tsx
@@ -5,6 +5,7 @@ import { useTableConfig } from "../../utils";
import { DataSourceFilter } from "@finos/vuu-data-types";
import { Input } from "@salt-ds/core";
import { getSchema } from "@finos/vuu-data-test";
+import { ActiveItemChangeHandler } from "packages/vuu-layout/src";
let displaySequence = 1;
@@ -31,6 +32,13 @@ export const DefaultFilterBar = ({
setFilters(filters);
}, []);
+ const handleChangeActiveFilterIndex = useCallback(
+ (index) => {
+ console.log(`filters changed ${index.join(",")}`);
+ },
+ []
+ );
+
useEffect(() => {
inputRef.current?.querySelector("input")?.focus();
}, []);
@@ -44,6 +52,7 @@ export const DefaultFilterBar = ({
}}
filters={filters}
onApplyFilter={handleApplyFilter}
+ onChangeActiveFilterIndex={handleChangeActiveFilterIndex}
onFiltersChanged={handleFiltersChanged}
tableSchema={tableSchema}
/>
diff --git a/vuu-ui/showcase/src/examples/Layout/OverflowContainer.examples.tsx b/vuu-ui/showcase/src/examples/Layout/OverflowContainer.examples.tsx
index 00f14bb79..62b7aae7b 100644
--- a/vuu-ui/showcase/src/examples/Layout/OverflowContainer.examples.tsx
+++ b/vuu-ui/showcase/src/examples/Layout/OverflowContainer.examples.tsx
@@ -282,7 +282,7 @@ export const VerticalOverflowContainerFlexLayout = () => {
style={{ background: "lightcyan", flex: 1, overflow: "hidden" }}
>
{/* prettier-ignore */}
-
+
1
2
3
diff --git a/vuu-ui/showcase/src/examples/Shell/AppHeader.examples.tsx b/vuu-ui/showcase/src/examples/Shell/AppHeader.examples.tsx
new file mode 100644
index 000000000..d4b0b82bb
--- /dev/null
+++ b/vuu-ui/showcase/src/examples/Shell/AppHeader.examples.tsx
@@ -0,0 +1,8 @@
+import { AppHeader } from "@finos/vuu-shell";
+
+let displaySequence = 1;
+
+export const DefaultAppHeader = () => {
+ return ;
+};
+DefaultAppHeader.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/Shell/AppSidePanel.examples.tsx b/vuu-ui/showcase/src/examples/Shell/AppSidePanel.examples.tsx
deleted file mode 100644
index c4fb71f37..000000000
--- a/vuu-ui/showcase/src/examples/Shell/AppSidePanel.examples.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { useVuuTables } from "@finos/vuu-data-react";
-import {
- DraggableLayout,
- Flexbox,
- LayoutProvider,
- Placeholder,
- View,
-} from "@finos/vuu-layout";
-import { SaltProvider } from "@salt-ds/core";
-import { AppSidePanel } from "app-vuu-example/src/app-sidepanel";
-import { useMockFeatureData } from "../utils/mock-data";
-import { useAutoLoginToVuuServer } from "../utils/useAutoLoginToVuuServer";
-
-let displaySequence = 1;
-
-export const DefaultAppSidePanel = () => {
- const { features, schemas } = useMockFeatureData();
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-DefaultAppSidePanel.displaySequence = displaySequence++;
-
-export const VuuConnectedAppSidePanel = () => {
- const error = useAutoLoginToVuuServer();
- const tables = useVuuTables();
- const { features } = useMockFeatureData();
-
- if (error) {
- return {error};
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-VuuConnectedAppSidePanel.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx b/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx
index aa00a8c25..0790df5b9 100644
--- a/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx
+++ b/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx
@@ -6,3 +6,38 @@ export const VerticalTabstrip = () => {
return ;
};
VerticalTabstrip.displaySequence = displaySequence++;
+
+export const VerticalTabstripCollapsed = () => {
+ return (
+
+ );
+};
+VerticalTabstripCollapsed.displaySequence = displaySequence++;
+
+export const VerticalTabstripCollapsedContent = () => {
+ return (
+
+ );
+};
+VerticalTabstripCollapsedContent.displaySequence = displaySequence++;
+
+export const VerticalTabstripContent = () => {
+ return (
+
+ );
+};
+VerticalTabstripContent.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/Shell/LoginPanel.examples.tsx b/vuu-ui/showcase/src/examples/Shell/LoginPanel.examples.tsx
new file mode 100644
index 000000000..29dda3843
--- /dev/null
+++ b/vuu-ui/showcase/src/examples/Shell/LoginPanel.examples.tsx
@@ -0,0 +1,5 @@
+import { LoginPanel } from "@finos/vuu-shell";
+
+export const DefaultLoginPanel = () => {
+ return ;
+};
diff --git a/vuu-ui/showcase/src/examples/Shell/Shell.examples.tsx b/vuu-ui/showcase/src/examples/Shell/Shell.examples.tsx
index 16dbca06a..fdf36c135 100644
--- a/vuu-ui/showcase/src/examples/Shell/Shell.examples.tsx
+++ b/vuu-ui/showcase/src/examples/Shell/Shell.examples.tsx
@@ -1,8 +1,5 @@
import { Shell } from "@finos/vuu-shell";
-import { AppSidePanel } from "app-vuu-example/src/app-sidepanel";
-import { CSSProperties, useMemo } from "react";
-import { useMockFeatureData } from "../utils/mock-data";
-import { useAutoLoginToVuuServer } from "../utils";
+import { CSSProperties } from "react";
import { AutoVuuTable } from "../html/HtmlTable.examples";
import { registerComponent } from "@finos/vuu-layout";
@@ -46,48 +43,3 @@ export const ShellWithDefaultLayout = () => {
};
ShellWithDefaultLayout.displaySequence = displaySequence++;
-
-export const ShellWithLeftPanel = () => {
- const { features, schemas } = useMockFeatureData();
- return (
- }
- loginUrl={window.location.toString()}
- user={user}
- style={
- {
- "--vuuShell-height": "100%",
- "--vuuShell-width": "100%",
- } as CSSProperties
- }
- />
- );
-};
-
-ShellWithLeftPanel.displaySequence = displaySequence++;
-
-export const ShellWithDefaultLayoutAndLeftPanel = () => {
- const error = useAutoLoginToVuuServer();
-
- const { features, schemas } = useMockFeatureData();
-
- if (error) {
- return error;
- }
-
- return (
- }
- loginUrl={window.location.toString()}
- user={user}
- style={
- {
- "--vuuShell-height": "100%",
- "--vuuShell-width": "100%",
- } as CSSProperties
- }
- />
- );
-};
-
-ShellWithDefaultLayoutAndLeftPanel.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/Shell/index.ts b/vuu-ui/showcase/src/examples/Shell/index.ts
index 4024fe33b..a0e1bb7d4 100644
--- a/vuu-ui/showcase/src/examples/Shell/index.ts
+++ b/vuu-ui/showcase/src/examples/Shell/index.ts
@@ -1,8 +1,9 @@
-export * as AppSidePanel from "./AppSidePanel.examples";
+export * as AppHeader from "./AppHeader.examples";
export * as ConnectionStatus from "./ConnectionStatus.examples";
export * as ConnectionMetrics from "./ConnectionMetrics.examples";
export * as Feature from "./Feature.examples";
export * as LeftNav from "./LeftNav.examples";
+export * as LoginPanel from "./LoginPanel.examples";
export * as SessionTableEditing from "./SessionTableEditing.examples";
export * as Shell from "./Shell.examples";
export * as ThemeProvider from "./ThemeProvider.examples";
diff --git a/vuu-ui/showcase/src/examples/Table/BasketTables.examples.tsx b/vuu-ui/showcase/src/examples/Table/BasketTables.examples.tsx
index d0b050d16..04bb2629d 100644
--- a/vuu-ui/showcase/src/examples/Table/BasketTables.examples.tsx
+++ b/vuu-ui/showcase/src/examples/Table/BasketTables.examples.tsx
@@ -1,126 +1,56 @@
-import { TableConfig } from "@finos/vuu-datagrid-types";
-import { TableNext } from "@finos/vuu-table";
-import { useState } from "react";
-import { useTableConfig } from "../utils";
+import { TableNext, TableProps } from "@finos/vuu-table";
+import { useMemo } from "react";
+import { BasketsTableName, getSchema, vuuModule } from "@finos/vuu-data-test";
+import { useVuuMenuActions } from "@finos/vuu-data-react";
+import { ContextMenuProvider } from "@finos/vuu-popups";
let displaySequence = 1;
-export const Basket = () => {
- const {
- typeaheadHook: _,
- config: configProp,
- ...props
- } = useTableConfig({
- table: { module: "BASKET", table: "basket" },
- });
-
- const [config, setConfig] = useState(configProp);
+const BasketTable = ({ tableName }: { tableName: BasketsTableName }) => {
+ const schema = getSchema(tableName);
- const handleConfigChange = (config: TableConfig) => {
- setConfig(config);
- };
-
- return (
- >(
+ () => ({
+ config: {
+ columns: schema.columns,
rowSeparators: true,
zebraStripes: true,
- }}
- onConfigChange={handleConfigChange}
- renderBufferSize={50}
- />
+ },
+ dataSource:
+ vuuModule("BASKET").createDataSource(tableName),
+ }),
+ [schema.columns, tableName]
);
-};
-Basket.displaySequence = displaySequence++;
-export const BasketConstituent = () => {
- const {
- typeaheadHook: _,
- config: configProp,
- ...props
- } = useTableConfig({
- table: { module: "BASKET", table: "basketConstituent" },
+ const { buildViewserverMenuOptions, handleMenuAction } = useVuuMenuActions({
+ dataSource: tableProps.dataSource,
});
- const [config, setConfig] = useState(configProp);
-
- const handleConfigChange = (config: TableConfig) => {
- setConfig(config);
- };
-
return (
-
+
+
+
);
};
-BasketConstituent.displaySequence = displaySequence++;
-export const BasketTrading = () => {
- const {
- typeaheadHook: _,
- config: configProp,
- ...props
- } = useTableConfig({
- table: { module: "BASKET", table: "basketTrading" },
- });
-
- const [config, setConfig] = useState(configProp);
+export const Basket = () => ;
+Basket.displaySequence = displaySequence++;
- const handleConfigChange = (config: TableConfig) => {
- setConfig(config);
- };
+export const BasketConstituent = () => (
+
+);
+BasketConstituent.displaySequence = displaySequence++;
- return (
-
- );
-};
+export const BasketTrading = () => ;
BasketTrading.displaySequence = displaySequence++;
-export const BasketTradingConstituent = () => {
- const {
- typeaheadHook: _,
- config: configProp,
- ...props
- } = useTableConfig({
- table: { module: "BASKET", table: "basketTradingConstituent" },
- });
-
- const [config, setConfig] = useState(configProp);
-
- const handleConfigChange = (config: TableConfig) => {
- setConfig(config);
- };
+export const AlgoType = () => ;
+AlgoType.displaySequence = displaySequence++;
- return (
-
- );
-};
-BasketTradingConstituent.displaySequence = displaySequence++;
+export const PriceStrategyType = () => (
+
+);
+PriceStrategyType.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/Table/SIMUL.examples.tsx b/vuu-ui/showcase/src/examples/Table/SIMUL.examples.tsx
new file mode 100644
index 000000000..2e6c0af21
--- /dev/null
+++ b/vuu-ui/showcase/src/examples/Table/SIMUL.examples.tsx
@@ -0,0 +1,88 @@
+import { useVuuMenuActions } from "@finos/vuu-data-react";
+import { getSchema, SimulTableName, vuuModule } from "@finos/vuu-data-test";
+import { ContextMenuProvider } from "@finos/vuu-popups";
+import { DefaultColumnConfiguration } from "@finos/vuu-shell";
+import { TableNext, TableProps } from "@finos/vuu-table";
+import { applyDefaultColumnConfig } from "@finos/vuu-utils";
+import { useMemo } from "react";
+
+let displaySequence = 1;
+
+const SimulTable = ({
+ getDefaultColumnConfig,
+ tableName,
+}: {
+ getDefaultColumnConfig?: DefaultColumnConfiguration;
+ tableName: SimulTableName;
+}) => {
+ const schema = getSchema(tableName);
+
+ const tableProps = useMemo>(
+ () => ({
+ config: {
+ columns: applyDefaultColumnConfig(schema, getDefaultColumnConfig),
+ rowSeparators: true,
+ zebraStripes: true,
+ },
+ dataSource:
+ vuuModule("SIMUL").createDataSource(tableName),
+ }),
+ [getDefaultColumnConfig, schema, tableName]
+ );
+
+ const { buildViewserverMenuOptions, handleMenuAction } = useVuuMenuActions({
+ dataSource: tableProps.dataSource,
+ });
+ return (
+
+
+
+ );
+};
+
+export const Instruments = () => ;
+Instruments.displaySequence = displaySequence++;
+
+export const Prices = () => {
+ const getDefaultColumnConfig = useMemo(
+ () => (tableName, columnName) => {
+ switch (columnName) {
+ case "ask":
+ case "bid":
+ return {
+ type: {
+ name: "number",
+ renderer: { name: "background", flashStyle: "arrow-bg" },
+ formatting: { decimals: 2, zeroPad: true },
+ },
+ };
+ case "askSize":
+ case "bidSize":
+ case "last":
+ case "open":
+ case "close":
+ return {
+ type: {
+ name: "number",
+ formatting: { decimals: 2, zeroPad: true },
+ },
+ };
+ }
+ },
+ []
+ );
+
+ return (
+
+ );
+};
+Prices.displaySequence = displaySequence++;
+
+export const Orders = () => ;
+Orders.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/Table/TableNext.examples.tsx b/vuu-ui/showcase/src/examples/Table/TableNext.examples.tsx
index 6ea4b97b3..af67c56f7 100644
--- a/vuu-ui/showcase/src/examples/Table/TableNext.examples.tsx
+++ b/vuu-ui/showcase/src/examples/Table/TableNext.examples.tsx
@@ -6,7 +6,7 @@ import {
View,
} from "@finos/vuu-layout";
import { ContextPanel } from "@finos/vuu-shell";
-import { TableNext } from "@finos/vuu-table";
+import { GroupHeaderCellNext, TableNext } from "@finos/vuu-table";
import {
ColumnSettingsPanel,
TableSettingsPanel,
@@ -14,42 +14,12 @@ import {
import { ColumnDescriptor, TableConfig } from "@finos/vuu-datagrid-types";
import { CSSProperties, useCallback, useMemo, useState } from "react";
import { useTableConfig, useTestDataSource } from "../utils";
-import { GroupHeaderCellNext } from "@finos/vuu-table";
import { getAllSchemas } from "@finos/vuu-data-test";
import "./TableNext.examples.css";
let displaySequence = 1;
-export const DefaultTableNextArrayData = () => {
- const {
- typeaheadHook: _,
- config: configProp,
- ...props
- } = useTableConfig({
- rangeChangeRowset: "full",
- table: { module: "SIMUL", table: "instruments" },
- });
-
- const [config, setConfig] = useState(configProp);
-
- const handleConfigChange = useCallback((config: TableConfig) => {
- setConfig(config);
- }, []);
-
- return (
-
- );
-};
-DefaultTableNextArrayData.displaySequence = displaySequence++;
-
export const NavigationStyle = () => {
const {
typeaheadHook: _,
@@ -110,6 +80,33 @@ export const EditableTableNextArrayData = () => {
},
},
},
+ lotSize: {
+ editable: true,
+ type: {
+ name: "number",
+ renderer: {
+ name: "input-cell",
+ },
+ },
+ },
+ exchange: {
+ editable: true,
+ type: {
+ name: "string",
+ renderer: {
+ name: "input-cell",
+ },
+ },
+ },
+ ric: {
+ editable: true,
+ type: {
+ name: "string",
+ renderer: {
+ name: "input-cell",
+ },
+ },
+ },
},
rangeChangeRowset: "full",
table: { module: "SIMUL", table: "instruments" },
@@ -124,7 +121,7 @@ export const EditableTableNextArrayData = () => {
dataSource={dataSource}
height={645}
renderBufferSize={10}
- width={723}
+ width={500}
/>
);
};
diff --git a/vuu-ui/showcase/src/examples/Table/index.ts b/vuu-ui/showcase/src/examples/Table/index.ts
index 26e3e95ed..c185e8ee1 100644
--- a/vuu-ui/showcase/src/examples/Table/index.ts
+++ b/vuu-ui/showcase/src/examples/Table/index.ts
@@ -4,3 +4,4 @@ export * as TableExtras from "./TableExtras.examples";
export * as TableList from "./TableList.examples";
export * as TableNext from "./TableNext.examples";
export * as BasketTables from "./BasketTables.examples";
+export * as SIMUL from "./SIMUL.examples";
diff --git a/vuu-ui/showcase/src/examples/TableExtras/ColumnSettings/ColumnSettings.examples.tsx b/vuu-ui/showcase/src/examples/TableExtras/ColumnSettings/ColumnSettings.examples.tsx
index fd2e73c95..d0d1f17c7 100644
--- a/vuu-ui/showcase/src/examples/TableExtras/ColumnSettings/ColumnSettings.examples.tsx
+++ b/vuu-ui/showcase/src/examples/TableExtras/ColumnSettings/ColumnSettings.examples.tsx
@@ -1,32 +1,48 @@
import { getSchema } from "@finos/vuu-data-test";
import { ColumnDescriptor, TableConfig } from "@finos/vuu-datagrid-types";
import {
- ColumnExpressionSubmitHandler,
ColumnFormattingPanel,
ColumnSettingsPanel,
} from "@finos/vuu-table-extras";
-import { CellRendererDescriptor } from "@finos/vuu-utils";
+import {
+ CellRendererDescriptor,
+ ColumnRenderPropsChangeHandler,
+} from "@finos/vuu-utils";
import { useCallback, useMemo, useState } from "react";
let displaySequence = 1;
export const ColumnFormattingPanelDouble = () => {
- const column = useMemo(
- () => ({
- name: "price",
- label: "Price",
- serverDataType: "double",
- }),
- []
- );
+ const [column, setColumn] = useState({
+ name: "price",
+ label: "Price",
+ serverDataType: "double",
+ });
const availableRenderers = useMemo(
() => [
{ name: "Default renderer (data type double)" },
{ name: "Background renderer" },
- { name: "Price Ticker" },
+ {
+ label: "Price Ticker",
+ name: "vuu.price-move-background",
+ },
],
+ []
+ );
+ const handleChangeRendering = useCallback(
+ (renderer) => {
+ console.log(`handleChangeRendering`, { renderer });
+ setColumn((col) => ({
+ ...col,
+ type: {
+ // TODO
+ ...col.type,
+ renderer,
+ },
+ }));
+ },
[]
);
@@ -35,8 +51,7 @@ export const ColumnFormattingPanelDouble = () => {
availableRenderers={availableRenderers}
column={column}
onChangeFormatting={() => console.log("onChangeFormatting")}
- onChangeRenderer={() => console.log("onChangeRenderer")}
- selectedCellRenderer={availableRenderers[0]}
+ onChangeRendering={handleChangeRendering}
style={{
border: "solid 1px lightgray",
margin: 20,
@@ -82,6 +97,10 @@ export const NewCalculatedColumnSettingsPanel = () => {
[]
);
+ const handleCancelCreateColumn = useCallback(() => {
+ console.log("cancel create column");
+ }, []);
+
return (
{
>
{
}>({
column: calculatedColumn,
tableConfig: {
- columns: schema.columns.concat(calculatedColumn),
+ columns: (schema.columns as ColumnDescriptor[]).concat(calculatedColumn),
},
});
const onConfigChange = (config: TableConfig) => {
@@ -136,6 +156,10 @@ export const CalculatedColumnSettingsPanel = () => {
}));
};
+ const handleCancelCreateColumn = useCallback(() => {
+ console.log("cancel create column");
+ }, []);
+
return (
{
>
{
- const schema = getSchema("basketTrading");
-
- const { dataSource: datasourceBasketTrading } = useTableConfig({
- dataSourceConfig: {
- columns: schema.columns.map((col) => col.name),
- },
- table: { module: "BASKET", table: "basketTrading" },
- });
-
- const { dataSource: datasourceBasketTradingSearch } = useTableConfig({
- dataSourceConfig: {
- columns: schema.columns.map((col) => col.name),
- },
- table: { module: "BASKET", table: "basketTrading" },
+ const [basket] = useState({
+ basketId: "basket-001",
+ basketName: "Test Basket",
+ filledPct: 0.7,
+ fxRateToUsd: 1.234,
+ totalNotional: 1_000_123,
+ totalNotionalUsd: 1_234_000,
+ units: 100,
});
+ const dataSource = vuuModule("BASKET").createDataSource("basketTrading");
const handleClickAddBasket = useCallback(() => {
console.log("Add Basket");
}, []);
+ const handleSelectBasket = useCallback(() => {
+ console.log("Select Basket");
+ }, []);
+
return (
);
};
diff --git a/vuu-ui/showcase/src/examples/VuuFeatures/BasketToolbar.examples.tsx b/vuu-ui/showcase/src/examples/VuuFeatures/BasketToolbar.examples.tsx
index c581d58b5..f7910b4ac 100644
--- a/vuu-ui/showcase/src/examples/VuuFeatures/BasketToolbar.examples.tsx
+++ b/vuu-ui/showcase/src/examples/VuuFeatures/BasketToolbar.examples.tsx
@@ -1,46 +1,55 @@
-import { getSchema } from "@finos/vuu-data-test";
+import { getSchema, vuuModule } from "@finos/vuu-data-test";
import { BasketToolbar } from "feature-basket-trading";
-import { useMemo, useState } from "react";
+import { useCallback, useMemo, useState } from "react";
import { BasketSelectorProps } from "sample-apps/feature-basket-trading/src/basket-selector";
+import { BasketChangeHandler } from "sample-apps/feature-basket-trading/src/basket-toolbar";
import { BasketStatus } from "sample-apps/feature-basket-trading/src/VuuBasketTradingFeature";
import { useTableConfig } from "../utils";
let displaySequence = 1;
export const BasketToolbarDesign = () => {
- const schema = getSchema("basketDefinitions");
const [basketStatus, setBasketStatus] = useState("design");
- const { dataSource: dataSourceBasket } = useTableConfig({
- count: 5,
- dataSourceConfig: {
- columns: schema.columns.map((col) => col.name),
- },
- table: { module: "SIMUL", table: "basketDefinitions" },
- });
+ const basket = useMemo(() => {
+ return {
+ basketId: ".FTSE",
+ basketName: "Test Basket",
+ filledPct: 0,
+ fxRateToUsd: 1.25,
+ totalNotional: 1000,
+ totalNotionalUsd: 1000,
+ units: 120,
+ };
+ }, []);
- const { dataSource: dataSourceBasketSearch } = useTableConfig({
- count: 5,
- dataSourceConfig: {
- columns: schema.columns.map((col) => col.name),
- },
- table: { module: "SIMUL", table: "basketDefinitions" },
- });
+ const dataSourceBasketTradingSearch =
+ vuuModule("BASKET").createDataSource("basketTrading");
const basketSelectorProps = useMemo(
() => ({
- basketId: "001",
- dataSourceBasket,
- dataSourceBasketSearch,
+ basket,
+ basketInstanceId: "123",
+ dataSourceBasketTradingSearch,
onClickAddBasket: () => console.log("Add Basket"),
+ onSelectBasket: () => undefined,
}),
- [dataSourceBasket, dataSourceBasketSearch]
+ [basket, dataSourceBasketTradingSearch]
+ );
+
+ const handleCommitBasketChange = useCallback(
+ (columnName, value) => {
+ console.log(`${columnName} => ${value}`);
+ },
+ []
);
return (
setBasketStatus("on-market")}
onTakeOffMarket={() => setBasketStatus("design")}
/>
diff --git a/vuu-ui/showcase/src/examples/VuuFeatures/BasketTradingFeature.examples.tsx b/vuu-ui/showcase/src/examples/VuuFeatures/BasketTradingFeature.examples.tsx
index e0775763d..6a7a4d5a9 100644
--- a/vuu-ui/showcase/src/examples/VuuFeatures/BasketTradingFeature.examples.tsx
+++ b/vuu-ui/showcase/src/examples/VuuFeatures/BasketTradingFeature.examples.tsx
@@ -1,10 +1,14 @@
-import { getSchema } from "@finos/vuu-data-test";
+import { getAllSchemas } from "@finos/vuu-data-test";
import { LayoutProvider, registerComponent, View } from "@finos/vuu-layout";
-import { Feature, FeatureProps, useLayoutManager } from "@finos/vuu-shell";
+import {
+ Feature,
+ FeatureProps,
+ LookupTableProvider,
+ ShellContextProvider,
+ useLayoutManager,
+} from "@finos/vuu-shell";
import { useCallback, useEffect } from "react";
import { BasketTradingFeature } from "../../features/BasketTrading.feature";
-import { BasketTradingNoBasketsFeature } from "../../features/BasketTradingNoBaskets.feature";
-import { BasketTradingOneBasketFeature } from "../../features/BasketTradingOneBasket.feature";
import { VuuBlotterHeader } from "./VuuBlotterHeader";
registerComponent("BasketTradingFeature", BasketTradingFeature, "view");
@@ -12,11 +16,7 @@ registerComponent("BasketTradingFeature", BasketTradingFeature, "view");
let displaySequence = 1;
export const DefaultBasketTradingFeature = () => {
- const basketSchema = getSchema("basket");
- // const basketDefinitionsSchema = getSchema("basketDefinitions");
- // const basketDesignSchema = getSchema("basketDesign");
- // const basketOrdersSchema = getSchema("basketOrders");
- const instrumentsSchema = getSchema("instruments");
+ const schemas = getAllSchemas();
//-----------------------------------------------------------------------------------
// Note the following functionality is provided by the Shell in a full application.
// Likewise the Shell provides the LayoutProvider wrapper. Again, in a full Vuu
@@ -37,140 +37,55 @@ export const DefaultBasketTradingFeature = () => {
);
// ----------------------------------------------------------------------------------
- return (
-
-
-
-
-
- );
-};
-DefaultBasketTradingFeature.displaySequence = displaySequence++;
-
-export const BasketTradingFeatureNoBaskets = () => {
- const basketSchema = getSchema("basket");
- // const basketDefinitionsSchema = getSchema("basketDefinitions");
- // const basketDesignSchema = getSchema("basketDesign");
- // const basketOrdersSchema = getSchema("basketOrders");
- const instrumentsSchema = getSchema("instruments");
-
- //-----------------------------------------------------------------------------------
- // Note the following functionality is provided by the Shell in a full application.
- // Likewise the Shell provides the LayoutProvider wrapper. Again, in a full Vuu
- // application, the Palette wraps each feature in a View.
- //-----------------------------------------------------------------------------------
- const { applicationLayout, saveApplicationLayout } = useLayoutManager();
-
- useEffect(() => {
- console.log(`%clayout changed`, "color: blue; font-weight: bold;");
- }, [applicationLayout]);
-
- const handleLayoutChange = useCallback(
- (layout) => {
- console.log("layout change");
- saveApplicationLayout(layout);
- },
- [saveApplicationLayout]
- );
- // ----------------------------------------------------------------------------------
-
- return (
-
-
-
-
-
- );
-};
-BasketTradingFeatureNoBaskets.displaySequence = displaySequence++;
-
-export const BasketTradingFeatureOneBasket = () => {
- const basketSchema = getSchema("basket");
- // const basketDefinitionsSchema = getSchema("basketDefinitions");
- // const basketDesignSchema = getSchema("basketDesign");
- // const basketOrdersSchema = getSchema("basketOrders");
- const instrumentsSchema = getSchema("instruments");
-
- //-----------------------------------------------------------------------------------
- // Note the following functionality is provided by the Shell in a full application.
- // Likewise the Shell provides the LayoutProvider wrapper. Again, in a full Vuu
- // application, the Palette wraps each feature in a View.
- //-----------------------------------------------------------------------------------
- const { applicationLayout, saveApplicationLayout } = useLayoutManager();
-
- useEffect(() => {
- console.log(`%clayout changed`, "color: blue; font-weight: bold;");
- }, [applicationLayout]);
-
- const handleLayoutChange = useCallback(
- (layout) => {
- console.log("layout change");
- saveApplicationLayout(layout);
- },
- [saveApplicationLayout]
- );
- // ----------------------------------------------------------------------------------
+ const getLookupValues = useCallback((table) => {
+ if (table.table === "algoType") {
+ return [
+ { label: "Sniper", value: 0 },
+ { label: "Dark Liquidity", value: 1 },
+ { label: "VWAP", value: 2 },
+ { label: "POV", value: 3 },
+ { label: "Dynamic CLose", value: 4 },
+ ];
+ } else if (table.table === "priceStrategyType") {
+ return [
+ { label: "Peg to Near Touch", value: 0 },
+ { label: "Far Touch", value: 1 },
+ { label: "Limit", value: 2 },
+ { label: "Algo", value: 3 },
+ ];
+ }
+ return [];
+ }, []);
return (
-
-
+
-
-
-
+
+
+
+
+
);
};
-BasketTradingFeatureOneBasket.displaySequence = displaySequence++;
+DefaultBasketTradingFeature.displaySequence = displaySequence++;
type Environment = "development" | "production";
const env = process.env.NODE_ENV as Environment;
@@ -186,11 +101,7 @@ const featurePropsForEnv: Record = {
export const BasketTradingFeatureAsFeature = () => {
const { url, css } = featurePropsForEnv[env];
- const basketSchema = getSchema("basket");
- // const basketDefinitionsSchema = getSchema("basketDefinitions");
- // const basketDesignSchema = getSchema("basketDesign");
- // const basketOrdersSchema = getSchema("basketOrders");
- const instrumentsSchema = getSchema("instruments");
+ const schemas = getAllSchemas();
return (
{
>
{
//-----------------------------------------------------------------------------------
const { applicationLayout, saveApplicationLayout } = useLayoutManager();
+ // Save layout into state so we can display in JsonTable
+ const [savedLayoutJson, setSavedLayoutJson] = useState(applicationLayout);
+
const handleLayoutChange = useCallback(
(layout) => {
saveApplicationLayout(layout);
+ setSavedLayoutJson(layout);
},
[saveApplicationLayout]
);
// ----------------------------------------------------------------------------------
return (
-
-
+
-
-
-
+
+
+
+
+
+
+
+
);
};
DefaultFilterTableFeature.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/VuuFeatures/NewBasketPanel.examples.tsx b/vuu-ui/showcase/src/examples/VuuFeatures/NewBasketPanel.examples.tsx
index 9db30ad87..2b553320c 100644
--- a/vuu-ui/showcase/src/examples/VuuFeatures/NewBasketPanel.examples.tsx
+++ b/vuu-ui/showcase/src/examples/VuuFeatures/NewBasketPanel.examples.tsx
@@ -1,7 +1,6 @@
-import { getSchema } from "@finos/vuu-data-test";
+import { createArrayDataSource, getSchema } from "@finos/vuu-data-test";
import { NewBasketPanel } from "feature-basket-trading";
import { useCallback, useMemo } from "react";
-import { createArrayDataSource } from "../utils/createArrayDataSource";
let displaySequence = 1;
diff --git a/vuu-ui/showcase/src/examples/html/HtmlTable.examples.tsx b/vuu-ui/showcase/src/examples/html/HtmlTable.examples.tsx
index adaf20f31..6172db7c8 100644
--- a/vuu-ui/showcase/src/examples/html/HtmlTable.examples.tsx
+++ b/vuu-ui/showcase/src/examples/html/HtmlTable.examples.tsx
@@ -8,7 +8,6 @@ import { SyntheticEvent, useCallback, useMemo, useState } from "react";
import { useTableConfig } from "../utils";
import {
DivElementKeyedWithTranslate,
- DivElementKeyedWithTranslateInlineScrollbarsCssVariables,
DivElementWithTranslate,
VuuTable,
} from "./html-table-components";
@@ -58,28 +57,6 @@ export const DefaultDivElementKeyedWithTranslate = () => {
};
DefaultDivElementKeyedWithTranslate.displaySequence = displaySequence++;
-export const DefaultDivElementKeyedWithTranslateInlineScrollbarsCssVariables =
- () => {
- const { typeaheadHook: _, ...config } = useTableConfig({
- columnCount: 10,
- count: 1000,
- rangeChangeRowset: "full",
- });
-
- return (
-
- );
- };
-DefaultDivElementKeyedWithTranslateInlineScrollbarsCssVariables.displaySequence =
- displaySequence++;
-
export const DefaultTableNext = () => {
const { typeaheadHook: _, ...config } = useTableConfig({
columnCount: 10,
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/DivElementKeyedWithTranslateInlineScrollbarsCssVariables.css b/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/DivElementKeyedWithTranslateInlineScrollbarsCssVariables.css
deleted file mode 100644
index 999ff429e..000000000
--- a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/DivElementKeyedWithTranslateInlineScrollbarsCssVariables.css
+++ /dev/null
@@ -1,126 +0,0 @@
-.DivElementKeyedWithTranslateInlineScrollbarsCssVariables {
-
-
- position: absolute;
- left: 95px;
- top: 45px;
- width: 715px;
- height: 645px;
- background-color: red;
- position: relative;
-}
-
-.DivElementKeyedWithTranslateInlineScrollbarsCssVariables-scrollbarContainer {
- --scroll-content-width: 1100px;
- border-bottom: none !important;
- border-top: none !important;
- border-left: solid 1px var(--salt-container-primary-borderColor);
- /* a top border */
- box-shadow: 0px -1px 0px 0px var(--salt-container-primary-borderColor);
- height: var(--viewport-body-height);
- left: 0px;
- overflow: auto;
- position: absolute;
- top: 30px;
- width: calc(var(--table-width) + 1px);
-}
-
-.DivElementKeyedWithTranslateInlineScrollbarsCssVariables-scrollbarContent {
- height: calc(var(--content-height) + var(--horizontal-scrollbar-height));
- position: absolute;
- width: var(--content-width);
-}
-
-.DivElementKeyedWithTranslateInlineScrollbarsCssVariables-contentContainer {
- --vuuTableHeaderHeight: 30px;
- background-color: var(--salt-container-primary-background);
- height: calc(var(--table-height) - var(--horizontal-scrollbar-height));
- position: relative;
- overflow: auto;
- overscroll-behavior: none;
- width: calc(var(--table-width) - var(--vertical-scrollbar-width));
-}
-
-.DivElementKeyedWithTranslateInlineScrollbarsCssVariables-contentContainer::-webkit-scrollbar {
- display: none;
-}
-
-
-.DivElementKeyedWithTranslateInlineScrollbarsCssVariables-table {
- position: absolute;
- top: 0;
- left: 0;
- table-layout: fixed;
- width: 1100px;
- margin: 0;
- border: none;
- background-color: #fff;
- border-collapse: separate;
- border-spacing: 0;
- border-left: 1px solid #ccc;
-
-}
-
-.DivElementKeyedWithTranslateInlineScrollbarsCssVariables-body {
- height: var(--content-height)
-}
-
-.DivElementKeyedWithTranslateInlineScrollbarsCssVariables-table [role="cell"] {
- display: inline-block;
-}
-
- .DivElementKeyedWithTranslateInlineScrollbarsCssVariables-col-headings {
- position: sticky;
- top: 0;
- /* ensure header row sits atop everything else when scrolling down */
- z-index: 1;
- }
-
- .DivElementKeyedWithTranslateInlineScrollbarsCssVariables-col-headers {
- white-space: nowrap;
- }
-
- .DivElementKeyedWithTranslateInlineScrollbarsCssVariables-col-header {
- border-bottom: 1px solid #ccc;
- box-sizing: border-box;
- line-height: 29px;
- background: #777;
- border-right: 1px solid #999;
- color: #fff;
- padding: 0 3px;
- }
-
- .DivElementKeyedWithTranslateInlineScrollbarsCssVariablesRow {
- background: #fff;
- border-right: 1px solid #ccc;
- border-bottom: 1px solid #ccc;
- box-sizing: border-box;
- height: 30px;
- line-height: 29px;
- position: absolute;
- top:0;
- white-space: nowrap;
- }
-
- /* make first column sticky when scrolling right */
- .DivElementKeyedWithTranslateInlineScrollbarsCssVariables-table [role="cell"]:first-child {
- position: sticky;
- left: 0;
- border-right-color: #aaa;
- background-color: white;
- }
-
- /* ensure first header cell sits atop everything else when scrolling right */
- .DivElementKeyedWithTranslateInlineScrollbarsCssVariables-table .DivElementKeyedWithTranslateInlineScrollbarsCssVariables-col-header:first-child {
- background-color: #777;
- position: sticky;
- left: 0;
- z-index: 2;
- }
-
- .sizer-cell {
- background-color: green !important;
- border: none !important;
- height: 0px;
- }
-
\ No newline at end of file
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/DivElementKeyedWithTranslateInlineScrollbarsCssVariables.tsx b/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/DivElementKeyedWithTranslateInlineScrollbarsCssVariables.tsx
deleted file mode 100644
index 0e2b6663f..000000000
--- a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/DivElementKeyedWithTranslateInlineScrollbarsCssVariables.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import { TableProps } from "@finos/vuu-table";
-import { CSSProperties } from "react";
-import { Row } from "../Row";
-import { useTable } from "./useTable";
-import { metadataKeys } from "@finos/vuu-utils";
-
-import "./DivElementKeyedWithTranslateInlineScrollbarsCssVariables.css";
-import { ContextMenuProvider } from "@finos/vuu-popups";
-import { HeaderCell } from "../HeaderCell";
-
-const classBase = "DivElementKeyedWithTranslateInlineScrollbarsCssVariables";
-
-const { IDX, RENDER_IDX } = metadataKeys;
-
-export const DivElementKeyedWithTranslateInlineScrollbarsCssVariables = ({
- className: classNameProp,
- config,
- dataSource,
- headerHeight = 25,
- height,
- id: idProp,
- onConfigChange,
- onFeatureEnabled,
- onFeatureInvocation,
- onSelect,
- onSelectionChange,
- onShowConfigEditor: onShowSettings,
- renderBufferSize = 0,
- rowHeight = 20,
- selectionModel = "extended",
- style: styleProp,
- width,
- ...htmlAttributes
-}: TableProps) => {
- const {
- columnMap,
- columns,
- containerMeasurements: { innerSize },
- data,
- handleContextMenuAction,
- menuBuilder,
- scrollProps,
- viewportMeasurements,
- ...tableProps
- } = useTable({
- config,
- dataSource,
- renderBufferSize,
- headerHeight,
- height,
- onConfigChange,
- onFeatureEnabled,
- onFeatureInvocation,
- onSelectionChange,
- rowHeight,
- selectionModel,
- width,
- });
-
- const style = {
- "--content-height": `${viewportMeasurements.contentHeight}px`,
- "--horizontal-scrollbar-height": `${viewportMeasurements.horizontalScrollbarHeight}px`,
- "--content-width": `${viewportMeasurements.contentWidth}px`,
- "--pinned-width-left": `${viewportMeasurements.pinnedWidthLeft}px`,
- "--pinned-width-right": `${viewportMeasurements.pinnedWidthRight}px`,
- "--header-height": `${headerHeight}px`,
- "--row-height": `${rowHeight}px`,
- "--table-height": `${innerSize?.height}px`,
- "--table-width": `${innerSize?.width}px`,
- "--total-header-height": `${viewportMeasurements.totalHeaderHeight}px`,
- "--vertical-scrollbar-width": `${viewportMeasurements.verticalScrollbarWidth}px`,
- "--viewport-body-height": `${viewportMeasurements.viewportBodyHeight}px`,
- } as CSSProperties;
-
- return (
-
-
-
-
-
-
-
- {columns.map((col, i) => (
-
- ))}
-
-
-
- {data.map((data) => (
-
- ))}
-
-
-
-
-
- );
-};
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/index.ts b/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/index.ts
deleted file mode 100644
index c97732067..000000000
--- a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./DivElementKeyedWithTranslateInlineScrollbarsCssVariables";
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useDataSource.ts b/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useDataSource.ts
deleted file mode 100644
index f0ad5c627..000000000
--- a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useDataSource.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { DataSource } from "@finos/vuu-data";
-import { VuuRange } from "@finos/vuu-protocol-types";
-import { DataSourceRow } from "@finos/vuu-data-types";
-import { useCallback, useEffect, useState } from "react";
-
-export const useDataSource = ({
- dataSource,
- initialRange,
-}: {
- dataSource: DataSource;
- initialRange: VuuRange;
-}) => {
- const [data, setData] = useState([]);
- useEffect(() => {
- dataSource?.subscribe({ range: initialRange }, (message) => {
- if (message.type === "viewport-update") {
- // if (message.size) {
- // console.log(`useDataSourcesize = ${message.size}`);
- // }
- if (message.rows) {
- setData(message.rows);
- }
- }
- });
- }, [dataSource, initialRange]);
-
- const setRange = useCallback(
- (range: VuuRange) => {
- dataSource.range = range;
- },
- [dataSource]
- );
-
- return {
- data,
- setRange,
- };
-};
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useInitialValue.ts b/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useInitialValue.ts
deleted file mode 100644
index e6d4a1d26..000000000
--- a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useInitialValue.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { useMemo, useRef } from "react";
-
-export const useInitialValue = (value: T): T => {
- const ref = useRef(value);
- return useMemo(() => ref.current, []);
-};
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useTable.ts b/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useTable.ts
deleted file mode 100644
index faf3184c9..000000000
--- a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useTable.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-import {
- DataSource,
- VuuFeatureInvocationMessage,
- VuuFeatureMessage,
-} from "@finos/vuu-data";
-import {
- GridConfig,
- SelectionChangeHandler,
- TableSelectionModel,
-} from "@finos/vuu-datagrid-types";
-import {
- MeasuredProps,
- useMeasuredContainer,
- useTableContextMenu,
- useTableViewport,
-} from "@finos/vuu-table";
-import { useContextMenu as usePopupContextMenu } from "@finos/vuu-popups";
-import { buildColumnMap } from "@finos/vuu-utils";
-import { MouseEvent, useCallback, useMemo, useState } from "react";
-import { useDataSource } from "./useDataSource";
-import { useTableModel } from "./useTableModel";
-import { useTableScroll } from "./useTableScroll";
-// import { useTableViewport } from "./useTableViewport";
-import { VuuRange } from "@finos/vuu-protocol-types";
-import { PersistentColumnAction } from "@finos/vuu-table/src/table/useTableModel";
-import { useInitialValue } from "./useInitialValue";
-import { useVirtualViewport } from "./useVirtualViewport";
-import { buildContextMenuDescriptors } from "@finos/vuu-table";
-
-export interface TableHookProps extends MeasuredProps {
- config: Omit;
- dataSource: DataSource;
- headerHeight: number;
- onConfigChange?: (config: Omit) => void;
- onFeatureEnabled?: (message: VuuFeatureMessage) => void;
- onFeatureInvocation?: (message: VuuFeatureInvocationMessage) => void;
- renderBufferSize?: number;
- rowHeight: number;
- onSelectionChange?: SelectionChangeHandler;
- selectionModel: TableSelectionModel;
-}
-
-export const useTable = ({
- config,
- dataSource,
- headerHeight,
- onConfigChange,
- onFeatureEnabled,
- onFeatureInvocation,
- onSelectionChange,
- rowHeight,
- selectionModel,
- ...measuredProps
-}: TableHookProps) => {
- const [rowCount] = useState(dataSource.size);
-
- if (dataSource === undefined) {
- throw Error("no data source provided to Vuu Table");
- }
-
- const menuBuilder = useMemo(
- () => buildContextMenuDescriptors(dataSource),
- [dataSource]
- );
-
- const containerMeasurements = useMeasuredContainer(measuredProps);
-
- const { columns, headings } = useTableModel(config, dataSource.config);
-
- const columnMap = useMemo(
- () => buildColumnMap(config.columns.map((col) => col.name)),
- [config.columns]
- );
-
- const {
- getRowAtPosition,
- getRowOffset,
- setPctScrollTop,
- ...viewportMeasurements
- } = useTableViewport({
- columns,
- headerHeight,
- headings,
- rowCount,
- rowHeight,
- // Note: innerSize will take border into account, whereas outerSize will not
- size: containerMeasurements.innerSize,
- });
-
- const initialRange = useInitialValue({
- from: 0,
- to: viewportMeasurements.rowCount + 1,
- });
-
- const { data, setRange } = useDataSource({
- dataSource,
- initialRange,
- });
-
- const handlePersistentColumnOperation = useCallback(
- (action: PersistentColumnAction) => {
- console.log(`handlePersistentColumnOperation ${JSON.stringify(action)}`);
- // expectConfigChangeRef.current = true;
- // dispatchColumnAction(action);
- },
- []
- );
-
- const handleContextMenuAction = useTableContextMenu({
- dataSource,
- onPersistentColumnOperation: handlePersistentColumnOperation,
- });
-
- const { onVerticalScroll } = useVirtualViewport({
- columns,
- getRowAtPosition,
- setRange,
- viewportMeasurements,
- });
-
- const handleVerticalScroll = useCallback(
- (scrollTop: number) => {
- onVerticalScroll(scrollTop);
- },
- [onVerticalScroll]
- );
-
- const { requestScroll, ...scrollProps } = useTableScroll({
- onVerticalScroll: handleVerticalScroll,
- viewportHeight: 645 - 30,
- });
-
- // TOSO ship this out into a hook
- const [showContextMenu] = usePopupContextMenu();
-
- const onContextMenu = useCallback(
- (evt: MouseEvent) => {
- // const { current: currentData } = dataRef;
- // const { current: currentDataSource } = dataSourceRef;
- const target = evt.target as HTMLElement;
- const cellEl = target?.closest("div[role='cell']");
- const rowEl = target?.closest("div[role='row']");
- console.log("onContextMenu", {
- cellEl,
- rowEl,
- });
- if (cellEl && rowEl /*&& currentData && currentDataSource*/) {
- // const { columns, selectedRowsCount } = currentDataSource;
- const columnMap = buildColumnMap(columns);
- // const rowIndex = parseInt(rowEl.ariaRowIndex ?? "-1");
- const cellIndex = Array.from(rowEl.childNodes).indexOf(cellEl);
- // const row = currentData.find(([idx]) => idx === rowIndex);
- const columnName = columns[cellIndex];
- showContextMenu(evt, "grid", {
- columnMap,
- columnName,
- // row,
- // selectedRows: selectedRowsCount === 0 ? NO_ROWS : getSelectedRows(),
- // viewport: dataSource?.viewport,
- });
- }
- },
- [columns, showContextMenu]
- );
-
- return {
- columnMap,
- columns,
- containerMeasurements,
- data,
- handleContextMenuAction,
- menuBuilder,
- onContextMenu,
- scrollProps,
- viewportMeasurements,
- };
-};
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useTableModel.ts b/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useTableModel.ts
deleted file mode 100644
index b717b85c1..000000000
--- a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useTableModel.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { DataSourceConfig } from "@finos/vuu-data";
-import {
- ColumnDescriptor,
- GridConfig,
- KeyedColumnDescriptor,
- TableHeadings,
-} from "@finos/vuu-datagrid-types";
-import { VuuColumnDataType } from "@finos/vuu-protocol-types";
-import { defaultValueFormatter, metadataKeys } from "@finos/vuu-utils";
-
-const NO_HEADINGS: TableHeadings = [];
-const DEFAULT_COLUMN_WIDTH = 100;
-const KEY_OFFSET = metadataKeys.count;
-
-const numericTypes = ["int", "long", "double"];
-const getDefaultAlignment = (serverDataType?: VuuColumnDataType) =>
- serverDataType === undefined
- ? undefined
- : numericTypes.includes(serverDataType)
- ? "right"
- : "left";
-
-const toKeyedColumWithDefaults =
- (options: Omit) =>
- (
- column: ColumnDescriptor & { key?: number },
- index: number
- ): KeyedColumnDescriptor => {
- const { columnDefaultWidth = DEFAULT_COLUMN_WIDTH } = options;
- const {
- align = getDefaultAlignment(column.serverDataType),
- key,
- name,
- label = name,
- width = columnDefaultWidth,
- ...rest
- } = column;
-
- const keyedColumnWithDefaults = {
- ...rest,
- align,
- // CellRenderer: getCellRendererForColumn(column),
- label,
- key: key ?? index + KEY_OFFSET,
- name,
- originalIdx: index,
- valueFormatter: defaultValueFormatter,
- width: width,
- };
-
- return keyedColumnWithDefaults;
- };
-
-export const useTableModel = (
- tableConfig: Omit,
- dataSourceConfig?: DataSourceConfig
-) => {
- const columns = tableConfig.columns.map(
- toKeyedColumWithDefaults(tableConfig)
- );
-
- return {
- columns,
- headings: NO_HEADINGS,
- };
-};
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useTableScroll.ts b/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useTableScroll.ts
deleted file mode 100644
index ad316d59b..000000000
--- a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useTableScroll.ts
+++ /dev/null
@@ -1,210 +0,0 @@
-import { useCallback, useRef } from "react";
-
-export interface ScrollRequestEnd {
- type: "scroll-end";
- direction: "home" | "end";
-}
-
-export interface ScrollRequestPage {
- type: "scroll-page";
- direction: "up" | "down";
-}
-
-export interface ScrollRequestDistance {
- type: "scroll-distance";
- distance: number;
-}
-
-export type ScrollRequest =
- | ScrollRequestPage
- | ScrollRequestDistance
- | ScrollRequestEnd;
-
-export type ScrollRequestHandler = (request: ScrollRequest) => void;
-
-const getPctScroll = (container: HTMLElement) => {
- const { scrollLeft, scrollTop } = container;
- const { clientHeight, clientWidth, scrollHeight, scrollWidth } = container;
- const pctScrollLeft = scrollLeft / (scrollWidth - clientWidth);
- const pctScrollTop = scrollTop / (scrollHeight - clientHeight);
- return [pctScrollLeft, pctScrollTop];
-};
-
-const getMaxScroll = (container: HTMLElement) => {
- const { clientHeight, clientWidth, scrollHeight, scrollWidth } = container;
- return [scrollWidth - clientWidth, scrollHeight - clientHeight];
-};
-
-interface CallbackRefHookProps {
- onAttach?: (el: T) => void;
- onDetach: (el: T) => void;
- label?: string;
-}
-
-const useCallbackRef = ({
- onAttach,
- onDetach,
-}: CallbackRefHookProps) => {
- const ref = useRef(null);
- const callbackRef = useCallback(
- (el: T | null) => {
- if (el) {
- ref.current = el;
- onAttach?.(el);
- } else if (ref.current) {
- const { current: originalRef } = ref;
- ref.current = el;
- onDetach?.(originalRef);
- }
- },
- [onAttach, onDetach]
- );
- return callbackRef;
-};
-
-export interface TableScrollHookProps {
- onHorizontalScroll?: (scrollLeft: number) => void;
- onVerticalScroll?: (scrollTop: number, pctScrollTop: number) => void;
- viewportHeight: number;
-}
-
-export const useTableScroll = ({
- onHorizontalScroll,
- onVerticalScroll,
-}: TableScrollHookProps) => {
- const contentContainerScrolledRef = useRef(false);
-
- const scrollPosRef = useRef({ scrollTop: 0, scrollLeft: 0 });
- const scrollbarContainerRef = useRef(null);
- const contentContainerRef = useRef(null);
-
- const maxScrollLeft = 435;
- const maxScrollTop = 29400;
-
- const handleScrollbarContainerScroll = useCallback(() => {
- const { current: contentContainer } = contentContainerRef;
- const { current: scrollbarContainer } = scrollbarContainerRef;
- const { current: contentContainerScrolled } = contentContainerScrolledRef;
- if (contentContainerScrolled) {
- contentContainerScrolledRef.current = false;
- } else if (contentContainer && scrollbarContainer) {
- const [pctScrollLeft, pctScrollTop] = getPctScroll(scrollbarContainer);
- const [maxScrollLeft, maxScrollTop] = getMaxScroll(contentContainer);
- const rootScrollLeft = Math.round(pctScrollLeft * maxScrollLeft);
- const rootScrollTop = Math.round(pctScrollTop * maxScrollTop);
- contentContainer.scrollTo({
- left: rootScrollLeft,
- top: rootScrollTop,
- behavior: "auto",
- });
- }
- }, []);
-
- const handleContentContainerScroll = useCallback(() => {
- const { current: contentContainer } = contentContainerRef;
- const { current: scrollbarContainer } = scrollbarContainerRef;
- const { current: scrollPos } = scrollPosRef;
-
- if (contentContainer && scrollbarContainer) {
- const { scrollLeft, scrollTop } = contentContainer;
- const [pctScrollLeft, pctScrollTop] = getPctScroll(contentContainer);
- contentContainerScrolledRef.current = true;
-
- scrollbarContainer.scrollLeft = Math.round(pctScrollLeft * maxScrollLeft);
- scrollbarContainer.scrollTop = Math.round(pctScrollTop * maxScrollTop);
-
- if (scrollPos.scrollTop !== scrollTop) {
- scrollPos.scrollTop = scrollTop;
- onVerticalScroll?.(scrollTop, pctScrollTop);
- }
- if (scrollPos.scrollLeft !== scrollLeft) {
- scrollPos.scrollLeft = scrollLeft;
- onHorizontalScroll?.(scrollLeft);
- }
- }
- }, [maxScrollLeft, maxScrollTop, onHorizontalScroll, onVerticalScroll]);
-
- const handleAttachScrollbarContainer = useCallback(
- (el: HTMLDivElement) => {
- scrollbarContainerRef.current = el;
- el.addEventListener("scroll", handleScrollbarContainerScroll, {
- passive: true,
- });
- },
- [handleScrollbarContainerScroll]
- );
-
- const handleDetachScrollbarContainer = useCallback(
- (el: HTMLDivElement) => {
- scrollbarContainerRef.current = null;
- el.removeEventListener("scroll", handleScrollbarContainerScroll);
- },
- [handleScrollbarContainerScroll]
- );
-
- const handleAttachContentContainer = useCallback(
- (el: HTMLDivElement) => {
- contentContainerRef.current = el;
- el.addEventListener("scroll", handleContentContainerScroll, {
- passive: true,
- });
- },
- [handleContentContainerScroll]
- );
-
- const handleDetachContentContainer = useCallback(
- (el: HTMLDivElement) => {
- contentContainerRef.current = null;
- el.removeEventListener("scroll", handleContentContainerScroll);
- },
- [handleContentContainerScroll]
- );
-
- const contentContainerCallbackRef = useCallbackRef({
- onAttach: handleAttachContentContainer,
- onDetach: handleDetachContentContainer,
- });
-
- const scrollbarContainerCallbackRef = useCallbackRef({
- onAttach: handleAttachScrollbarContainer,
- onDetach: handleDetachScrollbarContainer,
- });
-
- const requestScroll: ScrollRequestHandler = useCallback((scrollRequest) => {
- const { current: scrollbarContainer } = contentContainerRef;
- if (scrollbarContainer) {
- contentContainerScrolledRef.current = false;
- if (scrollRequest.type === "scroll-page") {
- const { clientHeight, scrollLeft, scrollTop } = scrollbarContainer;
- const { direction } = scrollRequest;
- const scrollBy = direction === "down" ? clientHeight : -clientHeight;
- const newScrollTop = Math.min(
- Math.max(0, scrollTop + scrollBy),
- maxScrollTop
- );
- scrollbarContainer.scrollTo({
- top: newScrollTop,
- left: scrollLeft,
- behavior: "auto",
- });
- } else if (scrollRequest.type === "scroll-end") {
- const { direction } = scrollRequest;
- const scrollTo = direction === "end" ? maxScrollTop : 0;
- scrollbarContainer.scrollTo({
- top: scrollTo,
- left: scrollbarContainer.scrollLeft,
- behavior: "auto",
- });
- }
- }
- }, []);
-
- return {
- /** Ref to be assigned to ScrollbarContainer */
- scrollbarContainerRef: scrollbarContainerCallbackRef,
- /** Ref to be assigned to ContentContainer */
- contentContainerRef: contentContainerCallbackRef,
- /** Scroll the table */
- requestScroll,
- };
-};
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useVirtualViewport.ts b/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useVirtualViewport.ts
deleted file mode 100644
index f4f125e01..000000000
--- a/vuu-ui/showcase/src/examples/html/html-table-components/div-element-keyed-with-translate-inline-scrollbars-css-variables/useVirtualViewport.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { KeyedColumnDescriptor } from "@finos/vuu-datagrid-types";
-import { VuuRange } from "@finos/vuu-protocol-types";
-import { RowAtPositionFunc } from "@finos/vuu-utils";
-import { ViewportMeasurements } from "@finos/vuu-table";
-import { useCallback, useRef } from "react";
-
-export interface VirtualViewportHookProps {
- columns: KeyedColumnDescriptor[];
- getRowAtPosition: RowAtPositionFunc;
- setRange: (range: VuuRange) => void;
- viewportMeasurements: ViewportMeasurements;
-}
-
-export const useVirtualViewport = ({
- getRowAtPosition,
- setRange,
- viewportMeasurements,
-}: VirtualViewportHookProps) => {
- const firstRowRef = useRef(-1);
- const { rowCount: viewportRowCount } = viewportMeasurements;
-
- const handleVerticalScroll = useCallback(
- (scrollTop: number) => {
- const firstRow = getRowAtPosition(scrollTop);
- if (firstRow !== firstRowRef.current) {
- firstRowRef.current = firstRow;
- setRange({ from: firstRow, to: firstRow + viewportRowCount });
- }
- },
- [getRowAtPosition, setRange, viewportRowCount]
- );
-
- return {
- onVerticalScroll: handleVerticalScroll,
- };
-};
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/index.ts b/vuu-ui/showcase/src/examples/html/html-table-components/index.ts
index ace499123..c5f47538b 100644
--- a/vuu-ui/showcase/src/examples/html/html-table-components/index.ts
+++ b/vuu-ui/showcase/src/examples/html/html-table-components/index.ts
@@ -1,4 +1,3 @@
export * from "./div-element-with-translate";
export * from "./div-element-keyed-with-translate";
-export * from "./div-element-keyed-with-translate-inline-scrollbars-css-variables";
export * from "./vuu-table";
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/vuu-table/VuuTable.tsx b/vuu-ui/showcase/src/examples/html/html-table-components/vuu-table/VuuTable.tsx
index 58ba3528d..7d52ab5a0 100644
--- a/vuu-ui/showcase/src/examples/html/html-table-components/vuu-table/VuuTable.tsx
+++ b/vuu-ui/showcase/src/examples/html/html-table-components/vuu-table/VuuTable.tsx
@@ -1,7 +1,6 @@
import { ContextMenuProvider } from "@finos/vuu-popups";
import { TablePropsDeprecated as TableProps } from "@finos/vuu-table";
import { isGroupColumn, metadataKeys } from "@finos/vuu-utils";
-import { useIdMemo } from "@salt-ds/core";
import { CSSProperties } from "react";
import { HeaderCell } from "../HeaderCell";
import { HeaderGroupCell } from "../HeaderGroupCell";
@@ -23,7 +22,6 @@ export const VuuTable = ({
height,
id: idProp,
onConfigChange,
- onFeatureEnabled,
onFeatureInvocation,
onSelect,
onSelectionChange,
@@ -58,7 +56,6 @@ export const VuuTable = ({
headerHeight,
height,
onConfigChange,
- onFeatureEnabled,
onFeatureInvocation,
onSelectionChange,
rowHeight,
diff --git a/vuu-ui/showcase/src/examples/html/html-table-components/vuu-table/useTableModel.ts b/vuu-ui/showcase/src/examples/html/html-table-components/vuu-table/useTableModel.ts
index 7c8d657be..1a9254dab 100644
--- a/vuu-ui/showcase/src/examples/html/html-table-components/vuu-table/useTableModel.ts
+++ b/vuu-ui/showcase/src/examples/html/html-table-components/vuu-table/useTableModel.ts
@@ -17,7 +17,6 @@ import {
isFilteredColumn,
isGroupColumn,
isPinned,
- isTypeDescriptor,
logger,
metadataKeys,
sortPinnedColumns,
@@ -36,12 +35,6 @@ const KEY_OFFSET = metadataKeys.count;
const columnWithoutDataType = ({ serverDataType }: ColumnDescriptor) =>
serverDataType === undefined;
-const getCellRendererForColumn = (column: ColumnDescriptor) => {
- if (isTypeDescriptor(column.type)) {
- return getCellRenderer(column.type?.renderer);
- }
-};
-
const getDataType = (
column: ColumnDescriptor,
columnNames: string[],
@@ -251,7 +244,7 @@ const toKeyedColumWithDefaults =
const keyedColumnWithDefaults = {
...rest,
align,
- CellRenderer: getCellRendererForColumn(column),
+ CellRenderer: getCellRenderer(column),
label: getLabel(label, columnFormatHeader),
key: key ?? index + KEY_OFFSET,
name,
diff --git a/vuu-ui/showcase/src/examples/utils/TickingArrayDataSource.ts b/vuu-ui/showcase/src/examples/utils/TickingArrayDataSource.ts
deleted file mode 100644
index 1f41da04c..000000000
--- a/vuu-ui/showcase/src/examples/utils/TickingArrayDataSource.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import {
- ArrayDataSource,
- ArrayDataSourceConstructorProps,
- SubscribeCallback,
- SubscribeProps,
-} from "@finos/vuu-data";
-import { VuuRange } from "@finos/vuu-protocol-types";
-import { DataSourceRow } from "@finos/vuu-data-types";
-import {
- RowUpdates,
- UpdateGenerator,
-} from "@finos/vuu-data-test/src/rowUpdates";
-
-export interface TickingArrayDataSourceConstructorProps
- extends ArrayDataSourceConstructorProps {
- updateGenerator?: UpdateGenerator;
-}
-
-export class TickingArrayDataSource extends ArrayDataSource {
- #updateGenerator: UpdateGenerator | undefined;
- constructor({
- updateGenerator,
- ...arrayDataSourceProps
- }: TickingArrayDataSourceConstructorProps) {
- super(arrayDataSourceProps);
- this.#updateGenerator = updateGenerator;
- updateGenerator?.setDataSource(this);
- updateGenerator?.setUpdateHandler(this.processUpdates);
- }
-
- async subscribe(subscribeProps: SubscribeProps, callback: SubscribeCallback) {
- const subscription = super.subscribe(subscribeProps, callback);
- if (subscribeProps.range) {
- this.#updateGenerator?.setRange(subscribeProps.range);
- }
- return subscription;
- }
-
- set range(range: VuuRange) {
- super.range = range;
- this.#updateGenerator?.setRange(range);
- }
- get range() {
- return super.range;
- }
-
- private processUpdates = (rowUpdates: RowUpdates[]) => {
- const updatedRows: DataSourceRow[] = [];
- const data = super.currentData;
- for (const [rowIndex, ...updates] of rowUpdates) {
- const row = data[rowIndex].slice() as DataSourceRow;
- if (row) {
- for (let i = 0; i < updates.length; i += 2) {
- const colIdx = updates[i] as number;
- const colVal = updates[i + 1];
- row[colIdx] = colVal;
- }
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- // TODO this is problematic if we're filtered
- // we need to update the correct underlying row
- data[rowIndex] = row;
- updatedRows.push(row);
- }
- }
- super._clientCallback?.({
- clientViewportId: super.viewport,
- mode: "update",
- rows: updatedRows,
- type: "viewport-update",
- });
- };
-}
diff --git a/vuu-ui/showcase/src/examples/utils/useTableConfig.ts b/vuu-ui/showcase/src/examples/utils/useTableConfig.ts
index 36a4c5720..0e7f898ce 100644
--- a/vuu-ui/showcase/src/examples/utils/useTableConfig.ts
+++ b/vuu-ui/showcase/src/examples/utils/useTableConfig.ts
@@ -9,7 +9,7 @@ import {
import { useMemo } from "react";
import { ArrayProxy } from "./ArrayProxy";
import { makeSuggestions } from "./makeSuggestions";
-import { TickingArrayDataSource } from "./TickingArrayDataSource";
+import { TickingArrayDataSource } from "@finos/vuu-data-test";
import { getColumnAndRowGenerator, populateArray } from "@finos/vuu-data-test";
const NO_CONFIG = {} as const;
diff --git a/vuu-ui/showcase/src/features/BasketTrading.feature.tsx b/vuu-ui/showcase/src/features/BasketTrading.feature.tsx
index 11b185dfe..fce62bbf7 100644
--- a/vuu-ui/showcase/src/features/BasketTrading.feature.tsx
+++ b/vuu-ui/showcase/src/features/BasketTrading.feature.tsx
@@ -6,35 +6,40 @@ import VuuBasketTradingFeature, {
import { useViewContext } from "@finos/vuu-layout";
import { TableSchema } from "@finos/vuu-data";
import { useMemo } from "react";
-import { createArrayDataSource } from "../examples/utils/createArrayDataSource";
+import { vuuModule, VuuModuleName } from "@finos/vuu-data-test";
export const BasketTradingFeature = ({
basketSchema,
- // basketDefinitionsSchema,
- // basketDesignSchema,
- // basketOrdersSchema,
+ basketTradingSchema,
+ basketTradingConstituentJoinSchema,
instrumentsSchema,
}: BasketTradingFeatureProps) => {
const { saveSession } = useViewContext();
useMemo(() => {
- const dataSourceConfig: [basketDataSourceKey, TableSchema, number?][] = [
- ["data-source-basket", basketSchema, 4],
- // ["data-source-basket-definitions", basketDefinitionsSchema, 5],
- // ["data-source-basket-definitions-search", basketDefinitionsSchema, 5],
- // ["data-source-basket-design", basketDesignSchema],
- // ["data-source-basket-orders", basketOrdersSchema],
- ["data-source-instruments", instrumentsSchema],
+ const dataSourceConfig: [
+ basketDataSourceKey,
+ TableSchema,
+ VuuModuleName
+ ][] = [
+ ["data-source-basket", basketSchema, "BASKET"],
+ ["data-source-basket-trading-control", basketTradingSchema, "BASKET"],
+ ["data-source-basket-trading-search", basketTradingSchema, "BASKET"],
+ [
+ "data-source-basket-trading-constituent-join",
+ basketTradingConstituentJoinSchema,
+ "BASKET",
+ ],
+ ["data-source-instruments", instrumentsSchema, "SIMUL"],
];
- for (const [key, schema, count] of dataSourceConfig) {
- const dataSource = createArrayDataSource({ count, table: schema.table });
+ for (const [key, schema, module] of dataSourceConfig) {
+ const dataSource = vuuModule(module).createDataSource(schema.table.table);
saveSession?.(dataSource, key);
}
}, [
basketSchema,
- // basketDefinitionsSchema,
- // basketDesignSchema,
- // basketOrdersSchema,
+ basketTradingConstituentJoinSchema,
+ basketTradingSchema,
instrumentsSchema,
saveSession,
]);
@@ -42,9 +47,8 @@ export const BasketTradingFeature = ({
return (
);
diff --git a/vuu-ui/showcase/src/features/BasketTradingNoBaskets.feature.tsx b/vuu-ui/showcase/src/features/BasketTradingNoBaskets.feature.tsx
deleted file mode 100644
index c814c5378..000000000
--- a/vuu-ui/showcase/src/features/BasketTradingNoBaskets.feature.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import VuuBasketTradingFeature, {
- BasketTradingFeatureProps,
- basketDataSourceKey,
-} from "feature-basket-trading";
-
-import { useViewContext } from "@finos/vuu-layout";
-import { TableSchema } from "@finos/vuu-data";
-import { useMemo } from "react";
-import { createArrayDataSource } from "../examples/utils/createArrayDataSource";
-
-export const BasketTradingNoBasketsFeature = ({
- basketSchema,
- // basketDefinitionsSchema,
- // basketDesignSchema,
- // basketOrdersSchema,
- instrumentsSchema,
-}: BasketTradingFeatureProps) => {
- const { saveSession } = useViewContext();
-
- useMemo(() => {
- const dataSourceConfig: [basketDataSourceKey, TableSchema, number?][] = [
- ["data-source-basket", basketSchema, 4],
- // ["data-source-basket-definitions", basketDefinitionsSchema, 0],
- // ["data-source-basket-definitions-search", basketDefinitionsSchema, 0],
- // ["data-source-basket-design", basketDesignSchema],
- // ["data-source-basket-orders", basketOrdersSchema],
- ["data-source-instruments", instrumentsSchema],
- ];
- for (const [key, schema, count] of dataSourceConfig) {
- const dataSource = createArrayDataSource({ count, table: schema.table });
- saveSession?.(dataSource, key);
- }
- }, [
- basketSchema,
- // basketDefinitionsSchema,
- // basketDesignSchema,
- // basketOrdersSchema,
- instrumentsSchema,
- saveSession,
- ]);
-
- return (
-
- );
-};
-
-export default BasketTradingNoBasketsFeature;
diff --git a/vuu-ui/showcase/src/features/BasketTradingOneBasket.feature.tsx b/vuu-ui/showcase/src/features/BasketTradingOneBasket.feature.tsx
deleted file mode 100644
index 56e669b9f..000000000
--- a/vuu-ui/showcase/src/features/BasketTradingOneBasket.feature.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import VuuBasketTradingFeature, {
- BasketTradingFeatureProps,
- basketDataSourceKey,
-} from "feature-basket-trading";
-
-import { useViewContext } from "@finos/vuu-layout";
-import { TableSchema } from "@finos/vuu-data";
-import { useMemo } from "react";
-import { createArrayDataSource } from "../examples/utils/createArrayDataSource";
-
-export const BasketTradingOneBasketFeature = ({
- basketSchema,
- // basketDefinitionsSchema,
- // basketDesignSchema,
- // basketOrdersSchema,
- instrumentsSchema,
-}: BasketTradingFeatureProps) => {
- const { saveSession } = useViewContext();
-
- useMemo(() => {
- const dataSourceConfig: [basketDataSourceKey, TableSchema, number?][] = [
- ["data-source-basket", basketSchema, 4],
- // ["data-source-basket-definitions", basketDefinitionsSchema, 1],
- // ["data-source-basket-definitions-search", basketDefinitionsSchema, 1],
- // ["data-source-basket-design", basketDesignSchema],
- // ["data-source-basket-orders", basketOrdersSchema],
- ["data-source-instruments", instrumentsSchema],
- ];
- for (const [key, schema, count] of dataSourceConfig) {
- const dataSource = createArrayDataSource({ count, table: schema.table });
- saveSession?.(dataSource, key);
- }
- }, [
- basketSchema,
- // basketDefinitionsSchema,
- // basketDesignSchema,
- // basketOrdersSchema,
- instrumentsSchema,
- saveSession,
- ]);
-
- return (
-
- );
-};
-
-export default BasketTradingOneBasketFeature;
diff --git a/vuu-ui/showcase/src/features/FilterTable.feature.tsx b/vuu-ui/showcase/src/features/FilterTable.feature.tsx
index b3949428e..43bf61ab5 100644
--- a/vuu-ui/showcase/src/features/FilterTable.feature.tsx
+++ b/vuu-ui/showcase/src/features/FilterTable.feature.tsx
@@ -1,21 +1,22 @@
import VuuFilterTableFeature, {
FilterTableFeatureProps,
} from "feature-vuu-filter-table";
+import { vuuModule, VuuModuleName } from "@finos/vuu-data-test";
import { useViewContext } from "@finos/vuu-layout";
-import { useTableConfig } from "../examples/utils";
+import { useMemo } from "react";
export const FilterTableFeature = ({
tableSchema,
}: FilterTableFeatureProps) => {
const { saveSession } = useViewContext();
- const { dataSource } = useTableConfig({
- count: 1000,
- dataSourceConfig: {
- columns: tableSchema.columns.map((col) => col.name),
- },
- table: tableSchema.table,
- rangeChangeRowset: "delta",
- });
+ const {
+ table: { module, table: tableName },
+ } = tableSchema;
+
+ const dataSource = useMemo(
+ () => vuuModule(module as VuuModuleName).createDataSource(tableName),
+ [module, tableName]
+ );
saveSession?.(dataSource, "data-source");
diff --git a/vuu/src/main/resources/logback-socket.xml b/vuu/src/main/resources/logback-socket.xml
index fd7209166..6693d38af 100644
--- a/vuu/src/main/resources/logback-socket.xml
+++ b/vuu/src/main/resources/logback-socket.xml
@@ -26,6 +26,10 @@
+
+
20MB
diff --git a/vuu/src/main/resources/runconfigurations/SimulMain.run.xml b/vuu/src/main/resources/runconfigurations/SimulMain.run.xml
index 2a66d37c8..758b0d727 100644
--- a/vuu/src/main/resources/runconfigurations/SimulMain.run.xml
+++ b/vuu/src/main/resources/runconfigurations/SimulMain.run.xml
@@ -1,11 +1,11 @@
-
+
-
+
diff --git a/vuu/src/main/scala/org/finos/vuu/core/CoreServerApiHandler.scala b/vuu/src/main/scala/org/finos/vuu/core/CoreServerApiHandler.scala
index 0062ed242..c31d32b2c 100644
--- a/vuu/src/main/scala/org/finos/vuu/core/CoreServerApiHandler.scala
+++ b/vuu/src/main/scala/org/finos/vuu/core/CoreServerApiHandler.scala
@@ -15,6 +15,17 @@ class CoreServerApiHandler(val viewPortContainer: ViewPortContainer,
val providers: ProviderContainer)(implicit timeProvider: Clock) extends ServerApi with StrictLogging {
+ override def process(msg: ViewPortRpcCall)(ctx: RequestContext): Option[ViewServerMessage] = {
+ Try(viewPortContainer.callRpcService(msg.vpId, msg.rpcName, msg.params, msg.namedParams, ctx.session)(ctx)) match {
+ case Success(action) =>
+ logger.info("Processed VP RPC call" + msg)
+ vsMsg(ViewPortMenuRpcResponse(msg.vpId, msg.rpcName, action))(ctx)
+ case Failure(e) =>
+ logger.info("Failed to remove viewport", e)
+ vsMsg(ViewPortMenuRpcReject(msg.vpId, msg.rpcName, e.getMessage))(ctx)
+ }
+ }
+
override def process(msg: ViewPortMenuCellRpcCall)(ctx: RequestContext): Option[ViewServerMessage] = {
Try(viewPortContainer.callRpcCell(msg.vpId, msg.rpcName, ctx.session, msg.rowKey, msg.field, msg.value)) match {
case Success(action) =>
diff --git a/vuu/src/main/scala/org/finos/vuu/core/module/ModuleFactory.scala b/vuu/src/main/scala/org/finos/vuu/core/module/ModuleFactory.scala
index 520e620dd..61af5f28d 100644
--- a/vuu/src/main/scala/org/finos/vuu/core/module/ModuleFactory.scala
+++ b/vuu/src/main/scala/org/finos/vuu/core/module/ModuleFactory.scala
@@ -47,35 +47,42 @@ case class ModuleFactoryNode protected(tableDefs: TableDefs,
vsName: String, staticServedResources: List[StaticServedResource],
rest: List[VuuServer => RestService],
viewPortDefs: Map[String, (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef],
- tableDefContainer: TableDefContainer = new TableDefContainer(Map())) {
+ tableDefContainer: TableDefContainer = new TableDefContainer(Map()),
+ var unrealizedViewPortDefs: Map[TableDefContainer => JoinTableDef, (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef]
+ ) {
def addTable(tableDef: TableDef, func: (DataTable, VuuServer) => Provider): ModuleFactoryNode = {
val noViewPortDefFunc = (dt: DataTable, prov: Provider, providerContainer: ProviderContainer, tableContainer: TableContainer) => NoViewPortDef
- ModuleFactoryNode(tableDefs.add(tableDef, func), rpc, vsName, staticServedResources, rest, viewPortDefs ++ Map(tableDef.name -> noViewPortDefFunc), tableDefContainer)
+ ModuleFactoryNode(tableDefs.add(tableDef, func), rpc, vsName, staticServedResources, rest, viewPortDefs ++ Map(tableDef.name -> noViewPortDefFunc), tableDefContainer, unrealizedViewPortDefs)
}
def addTable(tableDef: TableDef, func: (DataTable, VuuServer) => Provider, func2: (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef): ModuleFactoryNode = {
- ModuleFactoryNode(tableDefs.add(tableDef, func), rpc, vsName, staticServedResources, rest, viewPortDefs ++ Map(tableDef.name -> func2), tableDefContainer)
+ ModuleFactoryNode(tableDefs.add(tableDef, func), rpc, vsName, staticServedResources, rest, viewPortDefs ++ Map(tableDef.name -> func2), tableDefContainer, unrealizedViewPortDefs)
}
def addSessionTable(tableDef: TableDef, func2: (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef): ModuleFactoryNode = {
- ModuleFactoryNode(tableDefs.add(tableDef, (dt, vs) => NullProvider), rpc, vsName, staticServedResources, rest, viewPortDefs ++ Map(tableDef.name -> func2), tableDefContainer)
+ ModuleFactoryNode(tableDefs.add(tableDef, (dt, vs) => NullProvider), rpc, vsName, staticServedResources, rest, viewPortDefs ++ Map(tableDef.name -> func2), tableDefContainer, unrealizedViewPortDefs)
}
def addSessionTable(tableDef: TableDef): ModuleFactoryNode = {
- ModuleFactoryNode(tableDefs.add(tableDef, (dt, vs) => NullProvider), rpc, vsName, staticServedResources, rest, viewPortDefs, tableDefContainer)
+ ModuleFactoryNode(tableDefs.add(tableDef, (dt, vs) => NullProvider), rpc, vsName, staticServedResources, rest, viewPortDefs, tableDefContainer, unrealizedViewPortDefs)
}
def addJoinTable(func: TableDefContainer => JoinTableDef): ModuleFactoryNode = {
- ModuleFactoryNode(tableDefs.addJoin(func), rpc, vsName, staticServedResources, rest, viewPortDefs, tableDefContainer)
+ ModuleFactoryNode(tableDefs.addJoin(func), rpc, vsName, staticServedResources, rest, viewPortDefs, tableDefContainer, unrealizedViewPortDefs)
+ }
+
+ def addJoinTable(func: TableDefContainer => JoinTableDef, func2: (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef): ModuleFactoryNode = {
+ unrealizedViewPortDefs = unrealizedViewPortDefs ++ Map(func -> func2)
+ ModuleFactoryNode(tableDefs.addJoin(func), rpc, vsName, staticServedResources, rest, viewPortDefs, tableDefContainer, unrealizedViewPortDefs)
}
def addRpcHandler(rpcFunc: VuuServer => RpcHandler): ModuleFactoryNode = {
- ModuleFactoryNode(tableDefs, rpc ++ List(rpcFunc), vsName, staticServedResources, rest, viewPortDefs, tableDefContainer)
+ ModuleFactoryNode(tableDefs, rpc ++ List(rpcFunc), vsName, staticServedResources, rest, viewPortDefs, tableDefContainer, unrealizedViewPortDefs)
}
def addRestService(restFunc: VuuServer => RestService): ModuleFactoryNode = {
- ModuleFactoryNode(tableDefs, rpc, vsName, staticServedResources, rest ++ List(restFunc), viewPortDefs, tableDefContainer)
+ ModuleFactoryNode(tableDefs, rpc, vsName, staticServedResources, rest ++ List(restFunc), viewPortDefs, tableDefContainer, unrealizedViewPortDefs)
}
/**
@@ -86,7 +93,7 @@ case class ModuleFactoryNode protected(tableDefs: TableDefs,
* @param canBrowse can users browse the contents of the directory (listings)
*/
def addStaticResource(uriDirectory: String, path: Path, canBrowse: Boolean): ModuleFactoryNode = {
- ModuleFactoryNode(tableDefs, rpc, vsName, staticServedResources ++ List(StaticServedResource(uriDirectory, path, canBrowse)), rest, viewPortDefs, tableDefContainer)
+ ModuleFactoryNode(tableDefs, rpc, vsName, staticServedResources ++ List(StaticServedResource(uriDirectory, path, canBrowse)), rest, viewPortDefs, tableDefContainer, unrealizedViewPortDefs)
}
def asModule(): ViewServerModule = {
@@ -98,9 +105,16 @@ case class ModuleFactoryNode protected(tableDefs: TableDefs,
tableDefContainer.add(this.vsName, mutableTableDefs)
+ var joinViewPortDefs = Map[String, (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef]()
+
//we do this loop to allow join tables to depend on other realized join tables
tableDefs.joinDefs.foreach(toJTFunc => {
val realizedJoinTableDef = toJTFunc(tableDefContainer)
+ unrealizedViewPortDefs.get(toJTFunc) match {
+ case Some(viewPortDef) =>
+ joinViewPortDefs = joinViewPortDefs ++ Map(realizedJoinTableDef.name -> viewPortDef)
+ case None =>
+ }
mutableTableDefs = mutableTableDefs.addRealized(realizedJoinTableDef)
tableDefContainer.add(this.vsName, mutableTableDefs)
})
@@ -130,7 +144,7 @@ case class ModuleFactoryNode protected(tableDefs: TableDefs,
override def restServicesUnrealized: List[VuuServer => RestService] = rest
- override def viewPortDefs: Map[String, (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef] = parentRef.viewPortDefs
+ override def viewPortDefs: Map[String, (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef] = parentRef.viewPortDefs ++ joinViewPortDefs
}
}
@@ -143,7 +157,7 @@ object ModuleFactory {
implicit def stringToString(s: String): FieldDefString = new FieldDefString(s)
def withNamespace(ns: String)(implicit tableDefContainer: TableDefContainer): ModuleFactoryNode = {
- ModuleFactoryNode(TableDefs(List(), List(), List()), List(), ns, List(), List(), Map(), tableDefContainer)
+ ModuleFactoryNode(TableDefs(List(), List(), List()), List(), ns, List(), List(), Map(), tableDefContainer, Map())
}
}
diff --git a/vuu/src/main/scala/org/finos/vuu/core/module/basket/BasketModule.scala b/vuu/src/main/scala/org/finos/vuu/core/module/basket/BasketModule.scala
index 3046c8d44..20ea87f02 100644
--- a/vuu/src/main/scala/org/finos/vuu/core/module/basket/BasketModule.scala
+++ b/vuu/src/main/scala/org/finos/vuu/core/module/basket/BasketModule.scala
@@ -4,7 +4,7 @@ import org.finos.toolbox.lifecycle.LifecycleContainer
import org.finos.toolbox.time.Clock
import org.finos.vuu.api.{JoinSpec, JoinTableDef, JoinTo, LeftOuterJoin, Link, TableDef, ViewPortDef, VisualLinks}
import org.finos.vuu.core.module.basket.provider.{AlgoProvider, BasketConstituentProvider, BasketProvider, NullProvider, PriceStrategyProvider}
-import org.finos.vuu.core.module.basket.service.{BasketService, BasketTradingConstituentService}
+import org.finos.vuu.core.module.basket.service.{BasketService, BasketTradingConstituentJoinService, BasketTradingConstituentService}
import org.finos.vuu.core.module.price.PriceModule
import org.finos.vuu.core.module.simul.SimulationModule
import org.finos.vuu.core.module.{DefaultModule, ModuleFactory, TableDefContainer, ViewServerModule}
@@ -14,9 +14,11 @@ object BasketModule extends DefaultModule {
private final val NAME = "BASKET"
+ final val BasketTable = "basket"
final val BasketTradingTable = "basketTrading"
final val BasketConstituentTable = "basketConstituent"
final val BasketTradingConstituent = "basketTradingConstituent"
+ final val BasketTradingConstituentJoin = "basketTradingConstituentJoin"
def apply()(implicit clock: Clock, lifecycle: LifecycleContainer, tableDefContainer: TableDefContainer): ViewServerModule = {
@@ -30,7 +32,7 @@ object BasketModule extends DefaultModule {
.addTable(
//this table should contain one row for each of .FTSE, .DJI, .HSI, .etc...
TableDef(
- name = "basket",
+ name = BasketTable,
keyField = B.Id,
columns = Columns.fromNames(B.Id.string(), B.Name.string(), B.NotionalValue.double(), B.NotionalValueUsd.double()),
VisualLinks(),
@@ -113,7 +115,7 @@ object BasketModule extends DefaultModule {
)
.addJoinTable(tableDefs =>
JoinTableDef(
- name = "basketTrdConsPrices",
+ name = BasketTradingConstituentJoin,
baseTable = tableDefs.get(NAME, BasketTradingConstituent),
joinColumns = Columns.allFrom(tableDefs.get(NAME, BasketTradingConstituent)) ++ Columns.allFromExcept(tableDefs.get(PriceModule.NAME, "prices"), "ric"),
joins =
@@ -121,8 +123,13 @@ object BasketModule extends DefaultModule {
table = tableDefs.get(PriceModule.NAME, "prices"),
joinSpec = JoinSpec(left = "ric", right = "ric", LeftOuterJoin)
),
- joinFields = Seq()
- ))
+ joinFields = Seq(),
+ ),
+ (table, _, _, tableContainer) => ViewPortDef(
+ columns = table.getTableDef.columns,
+ service = new BasketTradingConstituentJoinService(table, tableContainer)
+ )
+ )
.asModule()
}
diff --git a/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala b/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala
index eef55ccd6..722ee686d 100644
--- a/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala
+++ b/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala
@@ -4,16 +4,21 @@ import com.typesafe.scalalogging.StrictLogging
import org.finos.toolbox.time.Clock
import org.finos.vuu.core.module.basket.BasketModule
import org.finos.vuu.core.module.basket.BasketModule.BasketConstituentTable
+import org.finos.vuu.core.module.basket.service.BasketService.counter
import org.finos.vuu.core.table.{DataTable, RowData, RowWithData, TableContainer}
-import org.finos.vuu.net.ClientSessionId
+import org.finos.vuu.net.{ClientSessionId, RequestContext}
import org.finos.vuu.net.rpc.RpcHandler
import org.finos.vuu.viewport.{NoAction, SelectionViewPortMenuItem, ViewPortAction, ViewPortMenu, ViewPortSelection}
import java.util.concurrent.atomic.AtomicInteger
+object BasketService{
+ val counter = new AtomicInteger(0)
+}
+
class BasketService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends RpcHandler with StrictLogging {
- private val counter = new AtomicInteger(0)
+//private val counter = new AtomicInteger(0)
import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingColumnNames => BT}
import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentColumnNames => BC}
@@ -43,17 +48,26 @@ class BasketService(val table: DataTable, val tableContainer: TableContainer)(im
RowWithData(instanceKey, Map(BT.InstanceId -> instanceKey, BT.Status -> "OFF-MARKET", BT.BasketId -> basketKey))
}
+ def createBasketFromRpc(basketKey: String, name: String)(ctx: RequestContext): ViewPortAction = {
+ createBasket(basketKey, name)
+ }
+
def createBasket(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = {
val basketKey = selection.rowKeyIndex.map({ case (key, _) => key }).toList.head
val instanceKey = getAndPadCounter(session)
+ createBasket(basketKey, instanceKey)
+ }
+
+ private def createBasket(basketKey: String, name: String): ViewPortAction = {
+
val constituents = getConstituentsForBasketKey(basketKey)
tableContainer.getTable(BasketModule.BasketTradingTable) match {
case table: DataTable =>
- table.processUpdate(instanceKey, mkTradingBasketRow(instanceKey, basketKey), clock.now())
+ table.processUpdate(name, mkTradingBasketRow(name, basketKey), clock.now())
case null =>
logger.error("Cannot find the Basket Trading table.")
}
@@ -61,11 +75,11 @@ class BasketService(val table: DataTable, val tableContainer: TableContainer)(im
tableContainer.getTable(BasketModule.BasketTradingConstituent) match {
case table: DataTable =>
constituents.foreach( rowData => {
- val constituentKey = instanceKey + "." + rowData.get(BTC.Ric)
+ val constituentKey = name + "." + rowData.get(BTC.Ric)
val weighting = rowData.get(BTC.Weighting).asInstanceOf[Double]
val quantity = (weighting * 100).asInstanceOf[Long]
val side = rowData.get(BTC.Side).toString
- table.processUpdate(constituentKey, mkTradingConstituentRow(side, basketKey, instanceKey, constituentKey, quantity, rowData), clock.now())
+ table.processUpdate(constituentKey, mkTradingConstituentRow(side, basketKey, name, constituentKey, quantity, rowData), clock.now())
})
case null =>
logger.error("Cannot find the Basket Trading Constituent.")
diff --git a/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala b/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala
new file mode 100644
index 000000000..5681d8ba7
--- /dev/null
+++ b/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala
@@ -0,0 +1,79 @@
+package org.finos.vuu.core.module.basket.service
+
+import com.typesafe.scalalogging.StrictLogging
+import org.finos.toolbox.time.Clock
+import org.finos.vuu.api.JoinTableDef
+import org.finos.vuu.core.module.basket.BasketModule.BasketTradingConstituentColumnNames.InstanceIdRic
+import org.finos.vuu.core.table.{DataTable, JoinTable, RowWithData, TableContainer}
+import org.finos.vuu.net.ClientSessionId
+import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler}
+import org.finos.vuu.viewport._
+
+class BasketTradingConstituentJoinService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends RpcHandler with EditRpcHandler with StrictLogging {
+
+ def onDeleteRow(key: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
+ ViewPortEditSuccess()
+ }
+
+ def onDeleteCell(key: String, column: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
+ ViewPortEditSuccess()
+ }
+
+ def onAddRow(key: String, data: Map[String, Any], vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
+ ViewPortEditSuccess()
+ }
+
+ private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
+ val joinTable = vp.table.asTable.asInstanceOf[JoinTable]
+ val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable
+ joinTable.sourceTables.get(baseTableDef.name) match {
+ case Some(table: DataTable) =>
+ table.processUpdate(key, RowWithData(key, Map(InstanceIdRic -> key, columnName -> data)), clock.now())
+ ViewPortEditSuccess()
+ case None =>
+ ViewPortEditFailure("Could not find base table")
+ }
+ }
+
+ private def onEditRow(key: String, row: Map[String, Any], vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
+ val table = vp.table.asTable
+ table.processUpdate(key, RowWithData(key, row), clock.now())
+ ViewPortEditSuccess()
+ }
+
+ private def onFormSubmit(vp: ViewPort, session: ClientSessionId): ViewPortAction = {
+// val table = vp.table.asTable
+// val primaryKeys = table.primaryKeys
+// val headKey = primaryKeys.head
+// val sequencerNumber = table.pullRow(headKey).get("sequenceNumber").asInstanceOf[Int].toLong
+//
+// if (sequencerNumber > 0) {
+// logger.info("I would now send this fix seq to a fix engine to reset, we're all good:" + sequencerNumber)
+// CloseDialogViewPortAction(vp.id)
+// } else {
+// logger.error("Seq number not set, returning error")
+// ViewPortEditFailure("Sequencer number has not been set.")
+// }
+ CloseDialogViewPortAction(vp.id)
+ }
+
+ private def onFormClose(vp: ViewPort, session: ClientSessionId): ViewPortAction = {
+ CloseDialogViewPortAction(vp.id)
+ }
+
+
+ override def deleteRowAction(): ViewPortDeleteRowAction = ViewPortDeleteRowAction("", this.onDeleteRow)
+
+ override def deleteCellAction(): ViewPortDeleteCellAction = ViewPortDeleteCellAction("", this.onDeleteCell)
+
+ override def addRowAction(): ViewPortAddRowAction = ViewPortAddRowAction("", this.onAddRow)
+
+ override def editCellAction(): ViewPortEditCellAction = ViewPortEditCellAction("", this.onEditCell)
+
+ override def editRowAction(): ViewPortEditRowAction = ViewPortEditRowAction("", this.onEditRow)
+
+ override def onFormSubmit(): ViewPortFormSubmitAction = ViewPortFormSubmitAction("", this.onFormSubmit)
+
+ override def onFormClose(): ViewPortFormCloseAction = ViewPortFormCloseAction("", this.onFormClose)
+
+}
diff --git a/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentService.scala b/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentService.scala
index 62bfe9240..faa21347a 100644
--- a/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentService.scala
+++ b/vuu/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentService.scala
@@ -2,6 +2,7 @@ package org.finos.vuu.core.module.basket.service
import com.typesafe.scalalogging.StrictLogging
import org.finos.toolbox.time.Clock
+import org.finos.vuu.core.module.basket.BasketModule.BasketTradingConstituentColumnNames.InstanceIdRic
import org.finos.vuu.core.table.{DataTable, RowWithData, TableContainer}
import org.finos.vuu.net.ClientSessionId
import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler}
@@ -23,7 +24,7 @@ class BasketTradingConstituentService(val table: DataTable, val tableContainer:
private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
val table = vp.table.asTable
- table.processUpdate(key, RowWithData(key, Map(columnName -> data)), clock.now())
+ table.processUpdate(key, RowWithData(key, Map(InstanceIdRic -> key, columnName -> data)), clock.now())
ViewPortEditSuccess()
}
diff --git a/vuu/src/main/scala/org/finos/vuu/net/ClientConnectionCreator.scala b/vuu/src/main/scala/org/finos/vuu/net/ClientConnectionCreator.scala
index 81cd8ef63..a5c3d1d6a 100644
--- a/vuu/src/main/scala/org/finos/vuu/net/ClientConnectionCreator.scala
+++ b/vuu/src/main/scala/org/finos/vuu/net/ClientConnectionCreator.scala
@@ -38,17 +38,17 @@ class DefaultMessageHandler(val channel: Channel,
sessionContainer: ClientSessionContainer,
moduleContainer: ModuleContainer)(implicit timeProvider: Clock) extends MessageHandler with StrictLogging {
- val closeFuture = channel.closeFuture()
+ val closeFuture: ChannelFuture = channel.closeFuture()
- closeFuture.addListener(new ChannelFutureListener {
- override def operationComplete(f: ChannelFuture): Unit = {
- logger.info("Calling disconnect() from future callback")
- disconnect()
- }
+ closeFuture.addListener((f: ChannelFuture) => {
+ logger.info("Calling disconnect() from future callback")
+ disconnect()
})
private def sendUpdatesInternal(updates: Seq[ViewPortUpdate], highPriority: Boolean = false) = {
- if (!updates.isEmpty) {
+ if (updates.nonEmpty) {
+
+ logger.debug(s"ASYNC-SVR-OUT: Sending ${updates.size} updates")
val formatted = formatDataOutbound(updates)
@@ -81,7 +81,7 @@ class DefaultMessageHandler(val channel: Channel,
}
}
- def disconnect() = {
+ def disconnect(): ChannelFuture = {
serverApi.disconnect(session)
sessionContainer.remove(session)
channel.disconnect()
@@ -91,19 +91,19 @@ class DefaultMessageHandler(val channel: Channel,
protected def formatDataOutbound(outbound: Seq[ViewPortUpdate]): TableRowUpdates = {
val updates = outbound.filter(vpu => vpu.vpRequestId == vpu.vp.getRequestId).flatMap(vp => formatOneRowUpdate(vp)).toArray
+ //val updates = outbound.flatMap(vp => formatOneRowUpdate(vp)).toArray
val updateId = RequestId.oneNew()
- TableRowUpdates(updateId, true, timeProvider.now, updates)
+ TableRowUpdates(updateId, isLast = true, timeProvider.now(), updates)
}
protected def formatOneRowUpdate(update: ViewPortUpdate): Option[RowUpdate] = {
update.vpUpdate match {
- case SizeUpdateType => {
+ case SizeUpdateType =>
//logger.debug(s"SVR[VP] Size: vpid=${update.vp.id} size=${update.vp.size}")
Some(RowUpdate(update.vpRequestId, update.vp.id, update.size, update.index, update.key.key, UpdateType.SizeOnly, timeProvider.now(), 0, Array.empty))
- }
case RowUpdateType =>
@@ -168,12 +168,12 @@ class DefaultMessageHandler(val channel: Channel,
case None =>
logger.error(s"Could not find impl for service ${rpc.service}")
Some(VsMsg(msg.requestId, msg.sessionId, msg.token, msg.user,
- RpcResponse(rpc.method, null, Error(s"Handler not found for rpc call ${rpc} for service ${rpc.service} in module ${msg.module}", -1)))
+ RpcResponse(rpc.method, null, Error(s"Handler not found for rpc call $rpc for service ${rpc.service} in module ${msg.module}", -1)))
)
}
case None =>
Some(VsMsg(msg.requestId, msg.sessionId, msg.token, msg.user,
- RpcResponse(rpc.method, null, Error(s"Handler not found for rpc call ${rpc} in module ${msg.module}", -1))))
+ RpcResponse(rpc.method, null, Error(s"Handler not found for rpc call $rpc in module ${msg.module}", -1))))
}
}
@@ -196,7 +196,7 @@ case class ClientSessionId(sessionId: String, user: String) extends Ordered[Clie
trait ClientSessionContainer {
- def register(sessionId: ClientSessionId, messageHandler: MessageHandler)
+ def register(sessionId: ClientSessionId, messageHandler: MessageHandler): Unit
//def addConnection(session: ClientSessionId, channel: Channel, handler: InboundMessageHandler): Unit
def getHandler(sessionId: ClientSessionId): Option[MessageHandler]
diff --git a/vuu/src/main/scala/org/finos/vuu/net/Messages.scala b/vuu/src/main/scala/org/finos/vuu/net/Messages.scala
index 359f54f5d..f4b204991 100644
--- a/vuu/src/main/scala/org/finos/vuu/net/Messages.scala
+++ b/vuu/src/main/scala/org/finos/vuu/net/Messages.scala
@@ -113,7 +113,8 @@ case class ChangeViewPortRangeSuccess(viewPortId: String, from: Int, to: Int) ex
case class OpenTreeNodeRequest(vpId: String, treeKey: String) extends MessageBody
-case class ViewPortMenuRpcCall()
+case class ViewPortRpcCall(vpId: String, rpcName: String, params: Array[Any], namedParams: Map[String, Any]) extends MessageBody
+//case class RpcCall(service: String, method: String, params: Array[Any], namedParams: Map[String, Any]) extends MessageBody
case class ViewPortMenuSelectionRpcCall(vpId: String, rpcName: String) extends MessageBody
diff --git a/vuu/src/main/scala/org/finos/vuu/net/ServerApi.scala b/vuu/src/main/scala/org/finos/vuu/net/ServerApi.scala
index 692539728..f1938bb46 100644
--- a/vuu/src/main/scala/org/finos/vuu/net/ServerApi.scala
+++ b/vuu/src/main/scala/org/finos/vuu/net/ServerApi.scala
@@ -42,7 +42,7 @@ trait ServerApi {
def process(msg: ViewPortMenuTableRpcCall)(ctx: RequestContext): Option[ViewServerMessage]
def process(msg: ViewPortMenuSelectionRpcCall)(ctx: RequestContext): Option[ViewServerMessage]
-
+ def process(msg: ViewPortRpcCall)(ctx: RequestContext): Option[ViewServerMessage]
def process(msg: ViewPortEditCellRpcCall)(ctx: RequestContext): Option[ViewServerMessage]
def process(msg: ViewPortEditRowRpcCall)(ctx: RequestContext): Option[ViewServerMessage]
diff --git a/vuu/src/main/scala/org/finos/vuu/net/rpc/RpcHandler.scala b/vuu/src/main/scala/org/finos/vuu/net/rpc/RpcHandler.scala
index b174b6420..b46143abc 100644
--- a/vuu/src/main/scala/org/finos/vuu/net/rpc/RpcHandler.scala
+++ b/vuu/src/main/scala/org/finos/vuu/net/rpc/RpcHandler.scala
@@ -4,13 +4,13 @@ import com.typesafe.scalalogging.StrictLogging
import org.finos.vuu.net._
import org.finos.vuu.viewport._
-import java.lang.reflect.Method
+import java.lang.reflect.{Method, Type}
trait RpcHandler extends StrictLogging {
def menuItems(): ViewPortMenu = EmptyViewPortMenu
- def menusAsMap() = {
+ def menusAsMap(): Map[String, ViewPortMenuItem] = {
val menus = menuItems()
@@ -32,13 +32,52 @@ trait RpcHandler extends StrictLogging {
foldMenus(menus)(Map())
}
- lazy val menuMap = menusAsMap()
+ lazy val menuMap: Map[String, ViewPortMenuItem] = menusAsMap()
def implementsService(serviceIf: String): Boolean = {
this.getClass.getInterfaces.exists(_.getSimpleName == serviceIf)
}
- val methodsAndParams = this.getClass.getMethods.map(method => (method.getName, method.getGenericParameterTypes, method)).groupBy(_._1).toMap
+ val methodsAndParams: Map[String, Array[(String, Array[Type], Method)]] = this.getClass.getMethods.map(method => (method.getName, method.getGenericParameterTypes, method)).groupBy(_._1)
+
+ def processViewPortRpcCall(methodName: String, params: Array[Any], namedParams: Map[String, Any])(ctx: RequestContext):ViewPortAction = {
+
+ if (!methodsAndParams.contains(methodName)) {
+ ViewPortRpcFailure(s"Could not find method $methodName")
+ } else {
+
+ val overloadedMethods = methodsAndParams(methodName)
+
+ val method = findBestMatchingMethod(methodName, params, overloadedMethods)
+
+ try {
+ val r = if (params.length == 0)
+ method.get.invoke(this, ctx)
+ else if (params.length == 1)
+ method.get.invoke(this, toO(params(0)), ctx)
+ else if (params.length == 2)
+ method.get.invoke(this, toO(params(0)), toO(params(1)), ctx)
+ else if (params.length == 3)
+ method.get.invoke(this, toO(params(0)), toO(params(1)), toO(params(2)), ctx)
+ else if (params.length == 4)
+ method.get.invoke(this, toO(params(0)), toO(params(1)), toO(params(2)), toO(params(3)), ctx)
+ else if (params.length == 5)
+ method.get.invoke(this, toO(params(0)), toO(params(1)), toO(params(2)), toO(params(3)), toO(params(4)), ctx)
+ else if (params.length == 6)
+ method.get.invoke(this, toO(params(0)), toO(params(1)), toO(params(2)), toO(params(3)), toO(params(4)), toO(params(5)), ctx)
+ else if (params.length == 7)
+ method.get.invoke(this, toO(params(0)), toO(params(1)), toO(params(2)), toO(params(3)), toO(params(4)), toO(params(5)), toO(params(6)), ctx)
+ else if (params.length == 8)
+ method.get.invoke(this, toO(params(0)), toO(params(1)), toO(params(2)), toO(params(3)), toO(params(4)), toO(params(5)), toO(params(6)), toO(params(7)), ctx)
+
+ r.asInstanceOf[ViewPortAction]
+ } catch {
+ case ex: Exception =>
+ logger.error(s"Exception occurred calling rpc $method", ex)
+ ViewPortRpcFailure(s"Exception occured calling rpc $method")
+ }
+ }
+ }
def processRpcCall(msg: ViewServerMessage, rpc: RpcCall)(ctx: RequestContext): Option[ViewServerMessage] = {
@@ -46,34 +85,34 @@ trait RpcHandler extends StrictLogging {
onError(s"error could not find method ${rpc.method}", 1)
} else {
- val overloadedMethods = methodsAndParams.get(rpc.method).get
+ val overloadedMethods = methodsAndParams(rpc.method)
val method = findBestMatchingMethod(rpc, overloadedMethods)
try{
- val r = if (rpc.params.size == 0)
+ val r = if (rpc.params.length == 0)
method.get.invoke(this, ctx)
- else if (rpc.params.size == 1)
+ else if (rpc.params.length == 1)
method.get.invoke(this, toO(rpc.params(0)), ctx)
- else if (rpc.params.size == 2)
+ else if (rpc.params.length == 2)
method.get.invoke(this, toO(rpc.params(0)), toO(rpc.params(1)), ctx)
- else if (rpc.params.size == 3)
+ else if (rpc.params.length == 3)
method.get.invoke(this, toO(rpc.params(0)), toO(rpc.params(1)), toO(rpc.params(2)), ctx)
- else if (rpc.params.size == 4)
+ else if (rpc.params.length == 4)
method.get.invoke(this, toO(rpc.params(0)), toO(rpc.params(1)), toO(rpc.params(2)), toO(rpc.params(3)), ctx)
- else if (rpc.params.size == 5)
+ else if (rpc.params.length == 5)
method.get.invoke(this, toO(rpc.params(0)), toO(rpc.params(1)), toO(rpc.params(2)), toO(rpc.params(3)), toO(rpc.params(4)), ctx)
- else if (rpc.params.size == 6)
+ else if (rpc.params.length == 6)
method.get.invoke(this, toO(rpc.params(0)), toO(rpc.params(1)), toO(rpc.params(2)), toO(rpc.params(3)), toO(rpc.params(4)), toO(rpc.params(5)), ctx)
- else if (rpc.params.size == 7)
+ else if (rpc.params.length == 7)
method.get.invoke(this, toO(rpc.params(0)), toO(rpc.params(1)), toO(rpc.params(2)), toO(rpc.params(3)), toO(rpc.params(4)), toO(rpc.params(5)), toO(rpc.params(6)), ctx)
- else if (rpc.params.size == 8)
+ else if (rpc.params.length == 8)
method.get.invoke(this, toO(rpc.params(0)), toO(rpc.params(1)), toO(rpc.params(2)), toO(rpc.params(3)), toO(rpc.params(4)), toO(rpc.params(5)), toO(rpc.params(6)), toO(rpc.params(7)), ctx)
Some(VsMsg(ctx.requestId, ctx.session.sessionId, ctx.token, ctx.session.user, RpcResponse(rpc.method, r, null), module = msg.module))
}catch{
case ex: Exception =>
- logger.error(s"Exception occurred calling rpc ${rpc}", ex)
+ logger.error(s"Exception occurred calling rpc $rpc", ex)
Some(VsMsg(ctx.requestId, ctx.session.sessionId, ctx.token, ctx.session.user, RpcResponse(rpc.method, null, Error(ex.getMessage, ex.hashCode())), module = msg.module))
}
@@ -85,9 +124,9 @@ trait RpcHandler extends StrictLogging {
def findBestMatchingMethod(rpc: RpcCall, methods: Array[(String, Array[java.lang.reflect.Type], Method)]): Option[Method] = {
- val size = rpc.params.size + 1
+ val size = rpc.params.length + 1
- val filteredBySize = methods.filter(_._2.size == size)
+ val filteredBySize = methods.filter(_._2.length == size)
filteredBySize.find(m => paramsEqualsRpcParams(m._3, rpc)) match {
case Some(tuple) => Some(tuple._3)
@@ -95,13 +134,34 @@ trait RpcHandler extends StrictLogging {
}
}
+ def findBestMatchingMethod(method: String, params: Array[Any], methods: Array[(String, Array[java.lang.reflect.Type], Method)]): Option[Method] = {
+
+ val size = params.length + 1
+
+ val filteredBySize = methods.filter(_._2.length == size)
+
+ filteredBySize.find(m => paramsEqualsRpcParams(m._3, params)) match {
+ case Some(tuple) => Some(tuple._3)
+ case None => None
+ }
+ }
+
+ def paramsEqualsRpcParams(method: Method, params: Array[Any]): Boolean = {
+
+ val assignable = params.zip(method.getGenericParameterTypes).map({ case (value, classParam) =>
+ classParam.getClass.isAssignableFrom(value.getClass)
+ })
+
+ assignable.length == params.length
+ }
+
def paramsEqualsRpcParams(method: Method, rpcCall: RpcCall): Boolean = {
val assignable = rpcCall.params.zip(method.getGenericParameterTypes).map({ case (value, classParam) =>
classParam.getClass.isAssignableFrom(value.getClass)
})
- assignable.size == rpcCall.params.size
+ assignable.length == rpcCall.params.length
}
def onError(message: String, code: Int): Option[ViewServerMessage] = {
diff --git a/vuu/src/main/scala/org/finos/vuu/provider/MockProvider.scala b/vuu/src/main/scala/org/finos/vuu/provider/MockProvider.scala
index cf28fd3d8..a9372e329 100644
--- a/vuu/src/main/scala/org/finos/vuu/provider/MockProvider.scala
+++ b/vuu/src/main/scala/org/finos/vuu/provider/MockProvider.scala
@@ -7,7 +7,7 @@ import org.finos.toolbox.time.Clock
import java.util.concurrent.ConcurrentHashMap
-class MockProvider(table: DataTable)(implicit timeProvider: Clock, lifecycle: LifecycleContainer) extends Provider with StrictLogging {
+class MockProvider(table: DataTable)(implicit clock: Clock, lifecycle: LifecycleContainer) extends Provider with StrictLogging {
lifecycle(this)
@@ -22,7 +22,7 @@ class MockProvider(table: DataTable)(implicit timeProvider: Clock, lifecycle: Li
}
def tick(key: String, row: Map[String, Any]) =
- table.processUpdate(key, new RowWithData(key, row), timeProvider.now())
+ table.processUpdate(key, new RowWithData(key, row), clock.now())
def delete(key: String) = {
table.processDelete(key)
diff --git a/vuu/src/main/scala/org/finos/vuu/provider/VuuJoinTableProvider.scala b/vuu/src/main/scala/org/finos/vuu/provider/VuuJoinTableProvider.scala
index 0dbeb885c..a25937791 100644
--- a/vuu/src/main/scala/org/finos/vuu/provider/VuuJoinTableProvider.scala
+++ b/vuu/src/main/scala/org/finos/vuu/provider/VuuJoinTableProvider.scala
@@ -214,7 +214,7 @@ class VuuJoinTableProvider(implicit timeProvider: Clock, lifecycle: LifecycleCon
//for each key in left table, send left update, including additional keys
leftKeys.foreach(key => {
- logger.debug(s"Publishing update for left key: ${key}")
+ logger.debug(s"Publishing update for left key: $key")
publishUpdateForLeftTableAndKey(joinTableDef, joinTable.asInstanceOf[JoinTable], joinTableDef.baseTable.name, key, joinSink.getEventDataSink(joinTableDef.baseTable.name).getEventState(key))
})
}
diff --git a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPort.scala b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPort.scala
index ee4c74723..8573ba501 100644
--- a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPort.scala
+++ b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPort.scala
@@ -214,6 +214,8 @@ class ViewPortImpl(val id: String,
val onlySortOrFilterChange = onlyFilterOrSortChanged(newStructuralFields, structuralFields.get())
+ logger.info(s"changeStructure(..) onlySortOrFilterChange=$onlySortOrFilterChange")
+
structuralFields.set(newStructuralFields)
if (!onlySortOrFilterChange)
@@ -277,6 +279,8 @@ class ViewPortImpl(val id: String,
val inrangeKeys = currentKeys.slice(from, to)
+ logger.info(s"Sending updates on ${inrangeKeys.length} inrangeKeys")
+
inrangeKeys.zip(from to to).foreach({ case (key, index) => publishHighPriorityUpdate(key, index) })
}
@@ -474,8 +478,8 @@ class ViewPortImpl(val id: String,
}
private def publishHighPriorityUpdate(key: String, index: Int): Unit = {
- logger.debug(s"publishing update @[$index] = $key ")
if (this.enabled) {
+ logger.debug(s"publishing update @[$index] = $key ")
outboundQ.pushHighPriority(ViewPortUpdate(this.requestId, this, table, RowKeyUpdate(key, table), index, RowUpdateType, this.keys.length, timeProvider.now()))
}
}
diff --git a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortContainer.scala b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortContainer.scala
index 4ecdc407c..5562dc27b 100644
--- a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortContainer.scala
+++ b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortContainer.scala
@@ -15,7 +15,7 @@ import org.finos.vuu.core.sort._
import org.finos.vuu.core.table.{DataTable, SessionTable, TableContainer}
import org.finos.vuu.core.tree.TreeSessionTableImpl
import org.finos.vuu.net.rpc.EditRpcHandler
-import org.finos.vuu.net.{ClientSessionId, FilterSpec, SortSpec}
+import org.finos.vuu.net.{ClientSessionId, FilterSpec, RequestContext, SortSpec}
import org.finos.vuu.provider.{Provider, ProviderContainer}
import org.finos.vuu.util.PublishQueue
import org.finos.vuu.viewport.tree._
@@ -45,10 +45,6 @@ trait ViewPortContainerMBean {
}
-object ViewPortContainerMetrics {
-
-}
-
class ViewPortContainer(val tableContainer: TableContainer, val providerContainer: ProviderContainer)(implicit timeProvider: Clock, metrics: MetricsProvider) extends RunInThread with StrictLogging with JmxAble with ViewPortContainerMBean {
import org.finos.vuu.core.VuuServerMetrics._
@@ -78,6 +74,12 @@ class ViewPortContainer(val tableContainer: TableContainer, val providerContaine
treeNodeStatesByVp.get(vpId)
}
+ def callRpcService(vpId: String, method: String, params: Array[Any], namedParams: Map[String, Any], session: ClientSessionId)(ctx: RequestContext): ViewPortAction = {
+ val viewPort = this.getViewPortById(vpId)
+ val viewPortDef = viewPort.getStructure.viewPortDef
+ viewPortDef.service.processViewPortRpcCall(method, params, namedParams)(ctx)
+ }
+
def callRpcCell(vpId: String, rpcName: String, session: ClientSessionId, rowKey: String, field: String, singleValue: Object): ViewPortAction = {
val viewPort = this.getViewPortById(vpId)
@@ -428,6 +430,9 @@ class ViewPortContainer(val tableContainer: TableContainer, val providerContaine
UserDefinedFilterAndSort(aFilter, aSort)
}
+ //update the viewport request id, to prevent any unwanted updates going out while we're changing the viewport
+ viewPort.setRequestId(requestId)
+
//we are not grouped by, but we want to change to a group by
if (viewPort.getGroupBy == NoGroupBy && groupBy != NoGroupBy) {
@@ -512,12 +517,16 @@ class ViewPortContainer(val tableContainer: TableContainer, val providerContaine
} else {
logger.info("[VP] default else condition in change() call")
- val structure = viewport.ViewPortStructuralFields(table = viewPort.table, columns = columns, viewPortDef = viewPort.getStructure.viewPortDef, filtAndSort = filtAndSort, filterSpec = filterSpec, groupBy = groupBy, viewPort.getTreeNodeStateStore, permissionChecker)
+ val structure = viewport.ViewPortStructuralFields(table = viewPort.table,
+ columns = columns, viewPortDef = viewPort.getStructure.viewPortDef,
+ filtAndSort = filtAndSort, filterSpec = filterSpec,
+ groupBy = groupBy, viewPort.getTreeNodeStateStore, permissionChecker
+ )
+ //viewPort.setRequestId(requestId)
viewPort.changeStructure(structure)
+ //viewPort.setKeys(viewPort.getKeys)
}
- viewPort.setRequestId(requestId)
-
viewPort
}
diff --git a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortEdit.scala b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortEdit.scala
index 2cbe9443d..5969e3356 100644
--- a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortEdit.scala
+++ b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortEdit.scala
@@ -4,6 +4,8 @@ import org.finos.vuu.net.ClientSessionId
trait ViewPortEditAction extends ViewPortAction {}
case class ViewPortEditSuccess() extends ViewPortEditAction {}
+case class ViewPortRpcSuccess() extends ViewPortAction {}
+case class ViewPortRpcFailure(msg: String) extends ViewPortAction {}
case class ViewPortEditFailure(msg: String) extends ViewPortEditAction {}
case class ViewPortEditCellAction(filter: String, func: (String, String, Object, ViewPort, ClientSessionId) => ViewPortEditAction){
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/AbstractViewPortTestCase.scala b/vuu/src/test/scala/org/finos/vuu/viewport/AbstractViewPortTestCase.scala
index 9898dc3f0..f3c2c52d8 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/AbstractViewPortTestCase.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/AbstractViewPortTestCase.scala
@@ -16,21 +16,19 @@ object TestTimeStamp{
class AbstractViewPortTestCase extends AnyFeatureSpec {
- implicit val timeProvider: Clock = new TestFriendlyClock(1311544800)
- implicit val metrics: MetricsProvider = new MetricsProviderImpl
def filterByVpId(vpUps: Seq[ViewPortUpdate], vp: ViewPort): Seq[ViewPortUpdate] = {
vpUps.filter( vpu => vpu.vp.id == vp.id )
}
- def setupViewPort(tableContainer: TableContainer, providerContainer: ProviderContainer): ViewPortContainer = {
+ def setupViewPort(tableContainer: TableContainer, providerContainer: ProviderContainer)(implicit clock: Clock, metrics: MetricsProvider): ViewPortContainer = {
val viewPortContainer = new ViewPortContainer(tableContainer, providerContainer)
viewPortContainer
}
- def createDefaultViewPortInfra(): (ViewPortContainer, DataTable, MockProvider, ClientSessionId, OutboundRowPublishQueue) = {
+ def createDefaultViewPortInfra()(implicit clock: Clock, metrics: MetricsProvider): (ViewPortContainer, DataTable, MockProvider, ClientSessionId, OutboundRowPublishQueue) = {
implicit val lifecycle: LifecycleContainer = new LifecycleContainer
val dateTime = 1437728400000L//new LocalDateTime(2015, 7, 24, 11, 0).toDateTime.toInstant.getMillis
@@ -83,7 +81,7 @@ class AbstractViewPortTestCase extends AnyFeatureSpec {
- def createDefaultOrderPricesViewPortInfra(): (ViewPortContainer, DataTable, MockProvider, DataTable, MockProvider, ClientSessionId, OutboundRowPublishQueue) = {
+ def createDefaultOrderPricesViewPortInfra()(implicit clock: Clock, metrics: MetricsProvider): (ViewPortContainer, DataTable, MockProvider, DataTable, MockProvider, ClientSessionId, OutboundRowPublishQueue) = {
implicit val lifecycle = new LifecycleContainer
val dateTime = 1437728400000L//new LocalDateTime(2015, 7, 24, 11, 0).toDateTime.toInstant.getMillis
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/CalculatedColumnsViewPortTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/CalculatedColumnsViewPortTest.scala
index 7c6bbfd64..e9de74afa 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/CalculatedColumnsViewPortTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/CalculatedColumnsViewPortTest.scala
@@ -1,5 +1,7 @@
package org.finos.vuu.viewport
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper.combineQs
import org.finos.vuu.core.table.ViewPortColumnCreator
@@ -10,128 +12,195 @@ import org.scalatest.prop.Tables.Table
class CalculatedColumnsViewPortTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen {
- Feature("Create a Viewport with calc on a non-existant column") {
+ implicit val clock: Clock = new TestFriendlyClock(TestTimeStamp.EPOCH_DEFAULT)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
- Given("we've created a viewport with orders in and a calc'd column 2")
- val (viewPortContainer, orders, ordersProvider, session, outQueue) = createDefaultViewPortInfra()
+ Feature("Create a Viewport with calc on a non-existant column") {
- val viewPortColumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric", "logicTest:String:=if(fooBar = 109, \"Yay\", \"Boo\")"))
+ Scenario("Scenario 1") {
- createNOrderRowsNoSleep(ordersProvider, 10)(timeProvider)
+ Given("we've created a viewport with orders in and a calc'd column 2")
+ val (viewPortContainer, orders, ordersProvider, session, outQueue) = createDefaultViewPortInfra()
- val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), viewPortColumns)
+ val viewPortColumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric", "logicTest:String:=if(fooBar = 109, \"Yay\", \"Boo\")"))
- viewPortContainer.runOnce()
+ createNOrderRowsNoSleep(ordersProvider, 10)(clock)
- val combinedUpdates = combineQs(viewPort)
+ val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), viewPortColumns)
- //this result is not ideal, need to fix, logic operators currently 'eat' the error message from the missing column
- //it should return a compound error
- assertVpEq(combinedUpdates) {
- Table(
- ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity","logicTest"),
- ("NYC-0000","chris" ,"VOD.L" ,1311544800L,100 ,"Boo" ),
- ("NYC-0001","chris" ,"VOD.L" ,1311544800L,101 ,"Boo" ),
- ("NYC-0002","chris" ,"VOD.L" ,1311544800L,102 ,"Boo" ),
- ("NYC-0003","chris" ,"VOD.L" ,1311544800L,103 ,"Boo" ),
- ("NYC-0004","chris" ,"VOD.L" ,1311544800L,104 ,"Boo" ),
- ("NYC-0005","chris" ,"VOD.L" ,1311544800L,105 ,"Boo" ),
- ("NYC-0006","chris" ,"VOD.L" ,1311544800L,106 ,"Boo" ),
- ("NYC-0007","chris" ,"VOD.L" ,1311544800L,107 ,"Boo" ),
- ("NYC-0008","chris" ,"VOD.L" ,1311544800L,108 ,"Boo" ),
- ("NYC-0009","chris" ,"VOD.L" ,1311544800L,109 ,"Boo" )
- )
+ viewPortContainer.runOnce()
+
+ val combinedUpdates = combineQs(viewPort)
+
+ //this result is not ideal, need to fix, logic operators currently 'eat' the error message from the missing column
+ //it should return a compound error
+ assertVpEq(combinedUpdates) {
+ Table(
+ ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity","logicTest"),
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ,"Boo" ),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800000L,101 ,"Boo" ),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800000L,102 ,"Boo" ),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800000L,103 ,"Boo" ),
+ ("NYC-0004","chris" ,"VOD.L" ,1311544800000L,104 ,"Boo" ),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800000L,105 ,"Boo" ),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800000L,106 ,"Boo" ),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800000L,107 ,"Boo" ),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800000L,108 ,"Boo" ),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800000L,109 ,"Boo" )
+ )
+ }
}
}
- Feature("Create a Viewport with logical calculated columns in") {
- Given("we've created a viewport with orders in and a calc'd column")
- val (viewPortContainer, orders, ordersProvider, session, outQueue) = createDefaultViewPortInfra()
+ Feature("Create a Viewport with logical calculated columns in") {
- val viewPortColumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric", "logicTest:String:=if(quantity = 109, \"Yay\", \"Boo\")"))
+ Scenario("Scenario 2") {
- createNOrderRowsNoSleep(ordersProvider, 10)(timeProvider)
+ val (viewPortContainer, orders, ordersProvider, session, outQueue) = createDefaultViewPortInfra()
- val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), viewPortColumns)
+ val viewPortColumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric", "logicTest:String:=if(quantity = 109, \"Yay\", \"Boo\")"))
- viewPortContainer.runOnce()
+ createNOrderRowsNoSleep(ordersProvider, 10)(clock)
- val combinedUpdates = combineQs(viewPort)
+ val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), viewPortColumns)
- assertVpEq(combinedUpdates) {
- Table(
- ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity","logicTest"),
- ("NYC-0000","chris" ,"VOD.L" ,1311544800L,100 ,"Boo" ),
- ("NYC-0001","chris" ,"VOD.L" ,1311544800L,101 ,"Boo" ),
- ("NYC-0002","chris" ,"VOD.L" ,1311544800L,102 ,"Boo" ),
- ("NYC-0003","chris" ,"VOD.L" ,1311544800L,103 ,"Boo" ),
- ("NYC-0004","chris" ,"VOD.L" ,1311544800L,104 ,"Boo" ),
- ("NYC-0005","chris" ,"VOD.L" ,1311544800L,105 ,"Boo" ),
- ("NYC-0006","chris" ,"VOD.L" ,1311544800L,106 ,"Boo" ),
- ("NYC-0007","chris" ,"VOD.L" ,1311544800L,107 ,"Boo" ),
- ("NYC-0008","chris" ,"VOD.L" ,1311544800L,108 ,"Boo" ),
- ("NYC-0009","chris" ,"VOD.L" ,1311544800L,109 ,"Yay" )
- )
+ viewPortContainer.runOnce()
+
+ val combinedUpdates = combineQs(viewPort)
+
+ assertVpEq(combinedUpdates) {
+ Table(
+ ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity","logicTest"),
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ,"Boo" ),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800000L,101 ,"Boo" ),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800000L,102 ,"Boo" ),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800000L,103 ,"Boo" ),
+ ("NYC-0004","chris" ,"VOD.L" ,1311544800000L,104 ,"Boo" ),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800000L,105 ,"Boo" ),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800000L,106 ,"Boo" ),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800000L,107 ,"Boo" ),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800000L,108 ,"Boo" ),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800000L,109 ,"Yay" )
+ )
+ }
}
}
- Feature("Create a Viewport with calculated columns in"){
+ Feature("Create a Viewport with calculated columns in") {
+
+ Scenario("Scenario 3") {
- Given("we've created a viewport with orders in and a calc'd column")
val (viewPortContainer, orders, ordersProvider, session, outQueue) = createDefaultViewPortInfra()
- val viewPortColumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric", "quantityTimes100:Long:=quantity*100"))
+ val viewPortColumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric", "quantityTimes100:Long:=quantity*100"))
- createNOrderRowsNoSleep(ordersProvider, 10)(timeProvider)
+ createNOrderRowsNoSleep(ordersProvider, 10)(clock)
val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), viewPortColumns)
- viewPortContainer.runOnce()
-
- val combinedUpdates = combineQs(viewPort)
-
- assertVpEq(combinedUpdates) {
- Table(
- ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity","quantityTimes100"),
- ("NYC-0000","chris" ,"VOD.L" ,1311544800L,100 ,10000 ),
- ("NYC-0001","chris" ,"VOD.L" ,1311544800L,101 ,10100 ),
- ("NYC-0002","chris" ,"VOD.L" ,1311544800L,102 ,10200 ),
- ("NYC-0003","chris" ,"VOD.L" ,1311544800L,103 ,10300 ),
- ("NYC-0004","chris" ,"VOD.L" ,1311544800L,104 ,10400 ),
- ("NYC-0005","chris" ,"VOD.L" ,1311544800L,105 ,10500 ),
- ("NYC-0006","chris" ,"VOD.L" ,1311544800L,106 ,10600 ),
- ("NYC-0007","chris" ,"VOD.L" ,1311544800L,107 ,10700 ),
- ("NYC-0008","chris" ,"VOD.L" ,1311544800L,108 ,10800 ),
- ("NYC-0009","chris" ,"VOD.L" ,1311544800L,109 ,10900 )
- )
+ viewPortContainer.runOnce()
+
+ val combinedUpdates = combineQs(viewPort)
+
+ assertVpEq(combinedUpdates) {
+ Table(
+ ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity","quantityTimes100"),
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ,10000 ),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800000L,101 ,10100 ),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800000L,102 ,10200 ),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800000L,103 ,10300 ),
+ ("NYC-0004","chris" ,"VOD.L" ,1311544800000L,104 ,10400 ),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800000L,105 ,10500 ),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800000L,106 ,10600 ),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800000L,107 ,10700 ),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800000L,108 ,10800 ),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800000L,109 ,10900 )
+ )
+ }
+
+
+ val viewPortColumns2 = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric", "textConcat:String:=concatenate(orderId, ric)"))
+
+ val viewPort2 = viewPortContainer.change(RequestId.oneNew(), session, viewPort.id, ViewPortRange(0, 10), viewPortColumns2)
+
+ viewPortContainer.runOnce()
+
+ val combinedUpdates2 = combineQs(viewPort2)
+
+ assertVpEq(combinedUpdates2) {
+ Table(
+ ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity","textConcat"),
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ,"NYC-0000VOD.L"),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800000L,101 ,"NYC-0001VOD.L"),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800000L,102 ,"NYC-0002VOD.L"),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800000L,103 ,"NYC-0003VOD.L"),
+ ("NYC-0004","chris" ,"VOD.L" ,1311544800000L,104 ,"NYC-0004VOD.L"),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800000L,105 ,"NYC-0005VOD.L"),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800000L,106 ,"NYC-0006VOD.L"),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800000L,107 ,"NYC-0007VOD.L"),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800000L,108 ,"NYC-0008VOD.L"),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800000L,109 ,"NYC-0009VOD.L")
+ )
+ }
}
+ }
- val viewPortColumns2 = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric", "textConcat:String:=concatenate(orderId, ric)"))
+ Feature("Amend a Viewport to include a calculated columns in") {
- val viewPort2 = viewPortContainer.change(RequestId.oneNew(), session, viewPort.id, ViewPortRange(0, 10), viewPortColumns2)
+ Scenario("Scenario 4") {
- viewPortContainer.runOnce()
+ val (viewPortContainer, orders, ordersProvider, session, outQueue) = createDefaultViewPortInfra()
- val combinedUpdates2 = combineQs(viewPort2)
+ val viewPortColumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric"))
- assertVpEq(combinedUpdates2) {
- Table(
- ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity","textConcat"),
- ("NYC-0000","chris" ,"VOD.L" ,1311544800L,100 ,"NYC-0000VOD.L"),
- ("NYC-0001","chris" ,"VOD.L" ,1311544800L,101 ,"NYC-0001VOD.L"),
- ("NYC-0002","chris" ,"VOD.L" ,1311544800L,102 ,"NYC-0002VOD.L"),
- ("NYC-0003","chris" ,"VOD.L" ,1311544800L,103 ,"NYC-0003VOD.L"),
- ("NYC-0004","chris" ,"VOD.L" ,1311544800L,104 ,"NYC-0004VOD.L"),
- ("NYC-0005","chris" ,"VOD.L" ,1311544800L,105 ,"NYC-0005VOD.L"),
- ("NYC-0006","chris" ,"VOD.L" ,1311544800L,106 ,"NYC-0006VOD.L"),
- ("NYC-0007","chris" ,"VOD.L" ,1311544800L,107 ,"NYC-0007VOD.L"),
- ("NYC-0008","chris" ,"VOD.L" ,1311544800L,108 ,"NYC-0008VOD.L"),
- ("NYC-0009","chris" ,"VOD.L" ,1311544800L,109 ,"NYC-0009VOD.L")
- )
- }
+ createNOrderRowsNoSleep(ordersProvider, 10)(clock)
- }
+ val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), viewPortColumns)
+
+ viewPortContainer.runOnce()
+
+ val combinedUpdates = combineQs(viewPort)
+
+ assertVpEq(combinedUpdates) {
+ Table(
+ ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800000L,101 ),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800000L,102 ),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800000L,103 ),
+ ("NYC-0004","chris" ,"VOD.L" ,1311544800000L,104 ),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800000L,105 ),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800000L,106 ),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800000L,107 ),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800000L,108 ),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800000L,109 )
+ )
+ }
+
+ val amendViewPortColumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric", "textConcat:String:=concatenate(orderId, ric)"))
+
+ val amendViewPort = viewPortContainer.change(RequestId.oneNew(), session, viewPort.id, ViewPortRange(0, 10), amendViewPortColumns)
+
+ val combinedUpdates2 = combineQs(amendViewPort)
+
+ assertVpEq(combinedUpdates2) {
+ Table(
+ ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity","textConcat"),
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ,"NYC-0000VOD.L"),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800000L,101 ,"NYC-0001VOD.L"),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800000L,102 ,"NYC-0002VOD.L"),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800000L,103 ,"NYC-0003VOD.L"),
+ ("NYC-0004","chris" ,"VOD.L" ,1311544800000L,104 ,"NYC-0004VOD.L"),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800000L,105 ,"NYC-0005VOD.L"),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800000L,106 ,"NYC-0006VOD.L"),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800000L,107 ,"NYC-0007VOD.L"),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800000L,108 ,"NYC-0008VOD.L"),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800000L,109 ,"NYC-0009VOD.L")
+ )
+ }
+ }
+ }
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/DeleteViewPortTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/DeleteViewPortTest.scala
index 86a6b6965..384c98b6d 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/DeleteViewPortTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/DeleteViewPortTest.scala
@@ -1,5 +1,7 @@
package org.finos.vuu.viewport
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper.combineQs
import org.finos.vuu.core.table.ViewPortColumnCreator
@@ -10,6 +12,9 @@ import org.scalatest.prop.Tables.Table
class DeleteViewPortTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen {
+ implicit val clock: Clock = new TestFriendlyClock(TestTimeStamp.EPOCH_DEFAULT)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+
Feature("Check when we delete a viewport, it is removed and no further ticks are sent") {
Scenario("Create viewport delete it, check no further ticks") {
@@ -18,7 +23,7 @@ class DeleteViewPortTest extends AbstractViewPortTestCase with Matchers with Giv
val vpcolumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric"))//.map(orders.getTableDef.columnForName(_)).toList
- createNOrderRows(ordersProvider, 10)(timeProvider)
+ createNOrderRows(ordersProvider, 10)(clock)
val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 4), vpcolumns)
@@ -30,11 +35,11 @@ class DeleteViewPortTest extends AbstractViewPortTestCase with Matchers with Giv
assertVpEq(combinedUpdates) {
Table(
- ("orderId", "trader", "ric", "tradeTime", "quantity"),
- ("NYC-0000", "chris", "VOD.L", 1311544800L, 100),
- ("NYC-0001", "chris", "VOD.L", 1311544810L, 101),
- ("NYC-0002", "chris", "VOD.L", 1311544820L, 102),
- ("NYC-0003", "chris", "VOD.L", 1311544830L, 103)
+ ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800010L,101 ),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 ),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800030L,103 )
)
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/OnlySendDiffRowsViewPortTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/OnlySendDiffRowsViewPortTest.scala
index 76c8cf2c4..052be3942 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/OnlySendDiffRowsViewPortTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/OnlySendDiffRowsViewPortTest.scala
@@ -1,5 +1,7 @@
package org.finos.vuu.viewport
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper.{combineQs, emptyQueues}
import org.finos.vuu.core.table.ViewPortColumnCreator
@@ -14,6 +16,9 @@ import org.scalatest.prop.Tables.Table
*/
class OnlySendDiffRowsViewPortTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen{
+ implicit val clock: Clock = new TestFriendlyClock(TestTimeStamp.EPOCH_DEFAULT)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+
Feature("Check when we update view port ranges, we only send the new rows"){
Scenario("Change viewport from 0-20 to 10-30 and check we only get 10 rows"){
@@ -22,7 +27,7 @@ class OnlySendDiffRowsViewPortTest extends AbstractViewPortTestCase with Matcher
val vpcolumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric"))//.map(orders.getTableDef.columnForName(_)).toList
- createNOrderRows(ordersProvider, 10)(timeProvider)
+ createNOrderRows(ordersProvider, 10)(clock)
val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 4), vpcolumns)
@@ -35,10 +40,10 @@ class OnlySendDiffRowsViewPortTest extends AbstractViewPortTestCase with Matcher
assertVpEq(combinedUpdates){
Table(
("orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- ("NYC-0000","chris" ,"VOD.L" ,1311544800L,100 ),
- ("NYC-0001","chris" ,"VOD.L" ,1311544810L,101 ),
- ("NYC-0002","chris" ,"VOD.L" ,1311544820L,102 ),
- ("NYC-0003","chris" ,"VOD.L" ,1311544830L,103 )
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800010L,101 ),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 ),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800030L,103 )
)
}
@@ -48,10 +53,9 @@ class OnlySendDiffRowsViewPortTest extends AbstractViewPortTestCase with Matcher
assertVpEq(combineQs(viewPortv2)){
Table(
- //don't send 2,3 as they already existed in client cache
("orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- ("NYC-0004","chris" ,"VOD.L" ,1311544840L,104 ),
- ("NYC-0005","chris" ,"VOD.L" ,1311544850L,105 )
+ ("NYC-0004","chris" ,"VOD.L" ,1311544800040L,104 ),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800050L,105 )
)
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/UpdateSelectionViewPortTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/UpdateSelectionViewPortTest.scala
index 00e134026..3f8dc9b02 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/UpdateSelectionViewPortTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/UpdateSelectionViewPortTest.scala
@@ -1,5 +1,7 @@
package org.finos.vuu.viewport
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper.combineQs
import org.finos.vuu.core.table.ViewPortColumnCreator
@@ -11,6 +13,9 @@ import org.scalatest.prop.Tables.Table
class UpdateSelectionViewPortTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen{
+ implicit val clock: Clock = new TestFriendlyClock(TestTimeStamp.EPOCH_DEFAULT)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+
Feature("Check our maintenance of selection on the server side") {
Scenario("create viewport, update selection, see selection come back") {
@@ -20,7 +25,7 @@ class UpdateSelectionViewPortTest extends AbstractViewPortTestCase with Matchers
val vpcolumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric"))
- createNOrderRows(ordersProvider, 10)(timeProvider)
+ createNOrderRows(ordersProvider, 10)(clock)
val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), vpcolumns)
@@ -30,18 +35,18 @@ class UpdateSelectionViewPortTest extends AbstractViewPortTestCase with Matchers
assertVpEqWithMeta(combinedUpdates) {
Table(
- ("sel","orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800L,100 ),
- (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544810L,101 ),
- (0 ,"NYC-0002","chris" ,"VOD.L" ,1311544820L,102 ),
- (0 ,"NYC-0003","chris" ,"VOD.L" ,1311544830L,103 ),
- (0 ,"NYC-0004","chris" ,"VOD.L" ,1311544840L,104 ),
- (0 ,"NYC-0005","chris" ,"VOD.L" ,1311544850L,105 ),
- (0 ,"NYC-0006","chris" ,"VOD.L" ,1311544860L,106 ),
- (0 ,"NYC-0007","chris" ,"VOD.L" ,1311544870L,107 ),
- (0 ,"NYC-0008","chris" ,"VOD.L" ,1311544880L,108 ),
- (0 ,"NYC-0009","chris" ,"VOD.L" ,1311544890L,109 )
- )
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544800010L,101 ),
+ (0 ,"NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 ),
+ (0 ,"NYC-0003","chris" ,"VOD.L" ,1311544800030L,103 ),
+ (0 ,"NYC-0004","chris" ,"VOD.L" ,1311544800040L,104 ),
+ (0 ,"NYC-0005","chris" ,"VOD.L" ,1311544800050L,105 ),
+ (0 ,"NYC-0006","chris" ,"VOD.L" ,1311544800060L,106 ),
+ (0 ,"NYC-0007","chris" ,"VOD.L" ,1311544800070L,107 ),
+ (0 ,"NYC-0008","chris" ,"VOD.L" ,1311544800080L,108 ),
+ (0 ,"NYC-0009","chris" ,"VOD.L" ,1311544800090L,109 )
+ )
}
And("we select some rows in the grid")
@@ -50,20 +55,20 @@ class UpdateSelectionViewPortTest extends AbstractViewPortTestCase with Matchers
Then("Check the selected rows is updated")
assertVpEqWithMeta(combineQs(viewPort)) {
Table(
- ("sel", "orderId", "trader", "ric", "tradeTime", "quantity"),
- (1, "NYC-0000", "chris", "VOD.L", 1311544800L, 100),
- (1, "NYC-0002", "chris", "VOD.L", 1311544820L, 102),
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ (1 ,"NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ (1 ,"NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 )
)
}
viewPortContainer.changeSelection(session, outQueue, viewPort.id, ViewPortSelectedIndices(Array(2)))
assertVpEqWithMeta(combineQs(viewPort)) {
- Table(
- ("sel", "orderId", "trader", "ric", "tradeTime", "quantity"),
- (0, "NYC-0000", "chris", "VOD.L", 1311544800L, 100),
- (1, "NYC-0002", "chris", "VOD.L", 1311544820L, 102)
- )
+ Table(
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ (1 ,"NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 )
+ )
}
And("when we apply a sort")
@@ -74,17 +79,17 @@ class UpdateSelectionViewPortTest extends AbstractViewPortTestCase with Matchers
Then("Check we still maintain the selection")
assertVpEqWithMeta(combineQs(viewPortChanged)) {
Table(
- ("sel", "orderId", "trader", "ric", "tradeTime", "quantity"),
- (0, "NYC-0000", "chris", "VOD.L", 1311544800L, 100),
- (0, "NYC-0001", "chris", "VOD.L", 1311544810L, 101),
- (1, "NYC-0002", "chris", "VOD.L", 1311544820L, 102),
- (0, "NYC-0003", "chris", "VOD.L", 1311544830L, 103),
- (0, "NYC-0004", "chris", "VOD.L", 1311544840L, 104),
- (0, "NYC-0005", "chris", "VOD.L", 1311544850L, 105),
- (0, "NYC-0006", "chris", "VOD.L", 1311544860L, 106),
- (0, "NYC-0007", "chris", "VOD.L", 1311544870L, 107),
- (0, "NYC-0008", "chris", "VOD.L", 1311544880L, 108),
- (0, "NYC-0009", "chris", "VOD.L", 1311544890L, 109)
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544800010L,101 ),
+ (1 ,"NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 ),
+ (0 ,"NYC-0003","chris" ,"VOD.L" ,1311544800030L,103 ),
+ (0 ,"NYC-0004","chris" ,"VOD.L" ,1311544800040L,104 ),
+ (0 ,"NYC-0005","chris" ,"VOD.L" ,1311544800050L,105 ),
+ (0 ,"NYC-0006","chris" ,"VOD.L" ,1311544800060L,106 ),
+ (0 ,"NYC-0007","chris" ,"VOD.L" ,1311544800070L,107 ),
+ (0 ,"NYC-0008","chris" ,"VOD.L" ,1311544800080L,108 ),
+ (0 ,"NYC-0009","chris" ,"VOD.L" ,1311544800090L,109 )
)
}
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/ViewPortListenerTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/ViewPortListenerTest.scala
index cf5023df5..c1a5031aa 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/ViewPortListenerTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/ViewPortListenerTest.scala
@@ -1,5 +1,7 @@
package org.finos.vuu.viewport
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper.combineQs
import org.finos.vuu.core.table.ViewPortColumnCreator
@@ -10,6 +12,9 @@ import org.scalatest.prop.Tables.Table
class ViewPortListenerTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen{
+ implicit val clock: Clock = new TestFriendlyClock(TestTimeStamp.EPOCH_DEFAULT)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+
Feature("Check that when we move a viewport range around in vp, the keys are correctly subscribed"){
Scenario("Check when we move vp down and back and tick, rows come through"){
@@ -20,7 +25,7 @@ class ViewPortListenerTest extends AbstractViewPortTestCase with Matchers with G
//val vpcolumns = List("orderId", "trader", "tradeTime", "quantity", "ric").map(orders.getTableDef.columnForName(_)).toList
- createNOrderRows(ordersProvider, 30)(timeProvider)
+ createNOrderRows(ordersProvider, 30)(clock)
val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), vpcolumns)
@@ -31,16 +36,16 @@ class ViewPortListenerTest extends AbstractViewPortTestCase with Matchers with G
assertVpEq(combinedUpdates){
Table(
("orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- ("NYC-0000","chris" ,"VOD.L" ,1311544800L,100 ),
- ("NYC-0001","chris" ,"VOD.L" ,1311544810L,101 ),
- ("NYC-0002","chris" ,"VOD.L" ,1311544820L,102 ),
- ("NYC-0003","chris" ,"VOD.L" ,1311544830L,103 ),
- ("NYC-0004","chris" ,"VOD.L" ,1311544840L,104 ),
- ("NYC-0005","chris" ,"VOD.L" ,1311544850L,105 ),
- ("NYC-0006","chris" ,"VOD.L" ,1311544860L,106 ),
- ("NYC-0007","chris" ,"VOD.L" ,1311544870L,107 ),
- ("NYC-0008","chris" ,"VOD.L" ,1311544880L,108 ),
- ("NYC-0009","chris" ,"VOD.L" ,1311544890L,109 )
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800010L,101 ),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 ),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800030L,103 ),
+ ("NYC-0004","chris" ,"VOD.L" ,1311544800040L,104 ),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800050L,105 ),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800060L,106 ),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800070L,107 ),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800080L,108 ),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800090L,109 )
)
}
@@ -53,16 +58,16 @@ class ViewPortListenerTest extends AbstractViewPortTestCase with Matchers with G
assertVpEq(combinedUpdates2){
Table(
("orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- ("NYC-0015","chris" ,"VOD.L" ,1311544950L,115 ),
- ("NYC-0016","chris" ,"VOD.L" ,1311544960L,116 ),
- ("NYC-0017","chris" ,"VOD.L" ,1311544970L,117 ),
- ("NYC-0018","chris" ,"VOD.L" ,1311544980L,118 ),
- ("NYC-0019","chris" ,"VOD.L" ,1311544990L,119 ),
- ("NYC-0020","chris" ,"VOD.L" ,1311545000L,120 ),
- ("NYC-0021","chris" ,"VOD.L" ,1311545010L,121 ),
- ("NYC-0022","chris" ,"VOD.L" ,1311545020L,122 ),
- ("NYC-0023","chris" ,"VOD.L" ,1311545030L,123 ),
- ("NYC-0024","chris" ,"VOD.L" ,1311545040L,124 )
+ ("NYC-0015","chris" ,"VOD.L" ,1311544800150L,115 ),
+ ("NYC-0016","chris" ,"VOD.L" ,1311544800160L,116 ),
+ ("NYC-0017","chris" ,"VOD.L" ,1311544800170L,117 ),
+ ("NYC-0018","chris" ,"VOD.L" ,1311544800180L,118 ),
+ ("NYC-0019","chris" ,"VOD.L" ,1311544800190L,119 ),
+ ("NYC-0020","chris" ,"VOD.L" ,1311544800200L,120 ),
+ ("NYC-0021","chris" ,"VOD.L" ,1311544800210L,121 ),
+ ("NYC-0022","chris" ,"VOD.L" ,1311544800220L,122 ),
+ ("NYC-0023","chris" ,"VOD.L" ,1311544800230L,123 ),
+ ("NYC-0024","chris" ,"VOD.L" ,1311544800240L,124 )
)
}
@@ -75,16 +80,16 @@ class ViewPortListenerTest extends AbstractViewPortTestCase with Matchers with G
assertVpEq(combinedUpdates3){
Table(
("orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- ("NYC-0005","chris" ,"VOD.L" ,1311544850L,105 ),
- ("NYC-0006","chris" ,"VOD.L" ,1311544860L,106 ),
- ("NYC-0007","chris" ,"VOD.L" ,1311544870L,107 ),
- ("NYC-0008","chris" ,"VOD.L" ,1311544880L,108 ),
- ("NYC-0009","chris" ,"VOD.L" ,1311544890L,109 ),
- ("NYC-0010","chris" ,"VOD.L" ,1311544900L,110 ),
- ("NYC-0011","chris" ,"VOD.L" ,1311544910L,111 ),
- ("NYC-0012","chris" ,"VOD.L" ,1311544920L,112 ),
- ("NYC-0013","chris" ,"VOD.L" ,1311544930L,113 ),
- ("NYC-0014","chris" ,"VOD.L" ,1311544940L,114 )
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800050L,105 ),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800060L,106 ),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800070L,107 ),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800080L,108 ),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800090L,109 ),
+ ("NYC-0010","chris" ,"VOD.L" ,1311544800100L,110 ),
+ ("NYC-0011","chris" ,"VOD.L" ,1311544800110L,111 ),
+ ("NYC-0012","chris" ,"VOD.L" ,1311544800120L,112 ),
+ ("NYC-0013","chris" ,"VOD.L" ,1311544800130L,113 ),
+ ("NYC-0014","chris" ,"VOD.L" ,1311544800140L,114 )
)
}
@@ -99,17 +104,17 @@ class ViewPortListenerTest extends AbstractViewPortTestCase with Matchers with G
assertVpEq(combinedUpdates4){
Table(
- ("orderId", "trader", "ric", "tradeTime", "quantity"),
- ("NYC-0014", "chris", "VOD.L", 1311544940L, 1014),
- ("NYC-0005", "chris", "VOD.L", 1311544850L, 1005),
- ("NYC-0006", "chris", "VOD.L", 1311544860L, 1006),
- ("NYC-0007", "chris", "VOD.L", 1311544870L, 1007),
- ("NYC-0008", "chris", "VOD.L", 1311544880L, 1008),
- ("NYC-0009", "chris", "VOD.L", 1311544890L, 1009),
- ("NYC-0010", "chris", "VOD.L", 1311544900L, 1010),
- ("NYC-0011", "chris", "VOD.L", 1311544910L, 1011),
- ("NYC-0012", "chris", "VOD.L", 1311544920L, 1012),
- ("NYC-0013", "chris", "VOD.L", 1311544930L, 1013)
+ ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ ("NYC-0014","chris" ,"VOD.L" ,1311544800140L,1014 ),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800050L,1005 ),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800060L,1006 ),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800070L,1007 ),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800080L,1008 ),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800090L,1009 ),
+ ("NYC-0010","chris" ,"VOD.L" ,1311544800100L,1010 ),
+ ("NYC-0011","chris" ,"VOD.L" ,1311544800110L,1011 ),
+ ("NYC-0012","chris" ,"VOD.L" ,1311544800120L,1012 ),
+ ("NYC-0013","chris" ,"VOD.L" ,1311544800130L,1013 )
)
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/VisualLinkedTreeViewPortTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/VisualLinkedTreeViewPortTest.scala
index b6bf75e1f..3aa087c96 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/VisualLinkedTreeViewPortTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/VisualLinkedTreeViewPortTest.scala
@@ -1,5 +1,7 @@
package org.finos.vuu.viewport
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper.combineQs
import org.finos.vuu.core.table.ViewPortColumnCreator
@@ -10,6 +12,9 @@ import org.scalatest.prop.Tables.Table
class VisualLinkedTreeViewPortTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen {
+ implicit val clock: Clock = new TestFriendlyClock(TestTimeStamp.EPOCH_DEFAULT)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+
Feature("Check our maintenance of selection on the server side") {
Scenario("create viewport, update selection, see selection come back") {
@@ -24,9 +29,9 @@ class VisualLinkedTreeViewPortTest extends AbstractViewPortTestCase with Matcher
createPricesRow(pricesProvider, "BT.L", 200, 201, 200.5, 199.5, "NYSE")
createPricesRow(pricesProvider, "BP.L", 300, 301, 300.5, 299.5, "XAMS")
- createNOrderRows(ordersProvider, 3)(timeProvider)
- createNOrderRows(ordersProvider, 4, ric = "BT.L", idOffset = 3)(timeProvider)
- createNOrderRows(ordersProvider, 5, ric = "BP.L", idOffset = 7)(timeProvider)
+ createNOrderRows(ordersProvider, 3)(clock)
+ createNOrderRows(ordersProvider, 4, ric = "BT.L", idOffset = 3)(clock)
+ createNOrderRows(ordersProvider, 5, ric = "BP.L", idOffset = 7)(clock)
val viewPortOrders = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), vpcolumnsOrders)
val viewPortPricesGroupBy = viewPortContainer.create(RequestId.oneNew(), session, outQueue, prices, ViewPortRange(0, 10), vpcolumnsPrices, groupBy = GroupBy(List(vpcolumnsPrices.getColumnForName("exchange").get), List()))
@@ -41,17 +46,17 @@ class VisualLinkedTreeViewPortTest extends AbstractViewPortTestCase with Matcher
assertVpEqWithMeta(orderUpdates) {
Table(
- ("sel", "orderId", "trader", "ric", "tradeTime", "quantity"),
- (0, "NYC-0000", "chris", "VOD.L", 1311544800L, 100),
- (0, "NYC-0001", "chris", "VOD.L", 1311544810L, 101),
- (0, "NYC-0002", "chris", "VOD.L", 1311544820L, 102),
- (0, "NYC-0003", "chris", "BT.L", 1311544830L, 100),
- (0, "NYC-0004", "chris", "BT.L", 1311544840L, 101),
- (0, "NYC-0005", "chris", "BT.L", 1311544850L, 102),
- (0, "NYC-0006", "chris", "BT.L", 1311544860L, 103),
- (0, "NYC-0007", "chris", "BP.L", 1311544870L, 100),
- (0, "NYC-0008", "chris", "BP.L", 1311544880L, 101),
- (0, "NYC-0009", "chris", "BP.L", 1311544890L, 102)
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544800010L,101 ),
+ (0 ,"NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 ),
+ (0 ,"NYC-0003","chris" ,"BT.L" ,1311544800030L,100 ),
+ (0 ,"NYC-0004","chris" ,"BT.L" ,1311544800040L,101 ),
+ (0 ,"NYC-0005","chris" ,"BT.L" ,1311544800050L,102 ),
+ (0 ,"NYC-0006","chris" ,"BT.L" ,1311544800060L,103 ),
+ (0 ,"NYC-0007","chris" ,"BP.L" ,1311544800070L,100 ),
+ (0 ,"NYC-0008","chris" ,"BP.L" ,1311544800080L,101 ),
+ (0 ,"NYC-0009","chris" ,"BP.L" ,1311544800090L,102 )
)
}
@@ -122,15 +127,15 @@ class VisualLinkedTreeViewPortTest extends AbstractViewPortTestCase with Matcher
assertVpEqWithMeta(filterByVpId(updates, viewPortOrders)) {
Table(
("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- (0 ,"NYC-0003","chris" ,"BT.L" ,1311544830L,100 ),
- (0 ,"NYC-0004","chris" ,"BT.L" ,1311544840L,101 ),
- (0 ,"NYC-0005","chris" ,"BT.L" ,1311544850L,102 ),
- (0 ,"NYC-0006","chris" ,"BT.L" ,1311544860L,103 ),
- (0 ,"NYC-0007","chris" ,"BP.L" ,1311544870L,100 ),
- (0 ,"NYC-0008","chris" ,"BP.L" ,1311544880L,101 ),
- (0 ,"NYC-0009","chris" ,"BP.L" ,1311544890L,102 ),
- (0 ,"NYC-0010","chris" ,"BP.L" ,1311544900L,103 ),
- (0 ,"NYC-0011","chris" ,"BP.L" ,1311544910L,104 )
+ (0 ,"NYC-0003","chris" ,"BT.L" ,1311544800030L,100 ),
+ (0 ,"NYC-0004","chris" ,"BT.L" ,1311544800040L,101 ),
+ (0 ,"NYC-0005","chris" ,"BT.L" ,1311544800050L,102 ),
+ (0 ,"NYC-0006","chris" ,"BT.L" ,1311544800060L,103 ),
+ (0 ,"NYC-0007","chris" ,"BP.L" ,1311544800070L,100 ),
+ (0 ,"NYC-0008","chris" ,"BP.L" ,1311544800080L,101 ),
+ (0 ,"NYC-0009","chris" ,"BP.L" ,1311544800090L,102 ),
+ (0 ,"NYC-0010","chris" ,"BP.L" ,1311544800100L,103 ),
+ (0 ,"NYC-0011","chris" ,"BP.L" ,1311544800110L,104 )
)
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/VisualLinkedViewPortTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/VisualLinkedViewPortTest.scala
index 677214430..1bbb22419 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/VisualLinkedViewPortTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/VisualLinkedViewPortTest.scala
@@ -1,5 +1,7 @@
package org.finos.vuu.viewport
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper.combineQs
import org.finos.vuu.core.table.ViewPortColumnCreator
@@ -15,6 +17,9 @@ class VisualLinkedViewPortTest extends AbstractViewPortTestCase with Matchers wi
Scenario("create viewport, update selection, see selection come back") {
+ implicit val clock: Clock = new TestFriendlyClock(TestTimeStamp.EPOCH_DEFAULT)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+
Given("we've created a viewport with orders in")
val (viewPortContainer, orders, ordersProvider, prices, pricesProvider, session, outQueue) = createDefaultOrderPricesViewPortInfra()
@@ -28,9 +33,9 @@ class VisualLinkedViewPortTest extends AbstractViewPortTestCase with Matchers wi
createPricesRow(pricesProvider, "BT.L", 200, 201, 200.5, 199.5)
createPricesRow(pricesProvider, "BP.L", 300, 301, 300.5, 299.5)
- createNOrderRows(ordersProvider, 3)(timeProvider)
- createNOrderRows(ordersProvider, 4, ric = "BT.L", idOffset = 3)(timeProvider)
- createNOrderRows(ordersProvider, 5, ric = "BP.L", idOffset = 7)(timeProvider)
+ createNOrderRows(ordersProvider, 3)(clock)
+ createNOrderRows(ordersProvider, 4, ric = "BT.L", idOffset = 3)(clock)
+ createNOrderRows(ordersProvider, 5, ric = "BP.L", idOffset = 7)(clock)
val viewPortOrders = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), vpcolumnsOrders)
val viewPortPrices = viewPortContainer.create(RequestId.oneNew(), session, outQueue, prices, ViewPortRange(0, 10), vpcolumnsPrices)
@@ -45,16 +50,16 @@ class VisualLinkedViewPortTest extends AbstractViewPortTestCase with Matchers wi
assertVpEqWithMeta(orderUpdates) {
Table(
("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800L,100 ),
- (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544810L,101 ),
- (0 ,"NYC-0002","chris" ,"VOD.L" ,1311544820L,102 ),
- (0 ,"NYC-0003","chris" ,"BT.L" ,1311544830L,100 ),
- (0 ,"NYC-0004","chris" ,"BT.L" ,1311544840L,101 ),
- (0 ,"NYC-0005","chris" ,"BT.L" ,1311544850L,102 ),
- (0 ,"NYC-0006","chris" ,"BT.L" ,1311544860L,103 ),
- (0 ,"NYC-0007","chris" ,"BP.L" ,1311544870L,100 ),
- (0 ,"NYC-0008","chris" ,"BP.L" ,1311544880L,101 ),
- (0 ,"NYC-0009","chris" ,"BP.L" ,1311544890L,102 )
+ (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544800010L,101 ),
+ (0 ,"NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 ),
+ (0 ,"NYC-0003","chris" ,"BT.L" ,1311544800030L,100 ),
+ (0 ,"NYC-0004","chris" ,"BT.L" ,1311544800040L,101 ),
+ (0 ,"NYC-0005","chris" ,"BT.L" ,1311544800050L,102 ),
+ (0 ,"NYC-0006","chris" ,"BT.L" ,1311544800060L,103 ),
+ (0 ,"NYC-0007","chris" ,"BP.L" ,1311544800070L,100 ),
+ (0 ,"NYC-0008","chris" ,"BP.L" ,1311544800080L,101 ),
+ (0 ,"NYC-0009","chris" ,"BP.L" ,1311544800090L,102 )
)
}
@@ -97,10 +102,10 @@ class VisualLinkedViewPortTest extends AbstractViewPortTestCase with Matchers wi
assertVpEqWithMeta(combineQs(viewPortOrders)) {
Table(
("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- (0 ,"NYC-0003","chris" ,"BT.L" ,1311544830L,100 ),
- (0 ,"NYC-0004","chris" ,"BT.L" ,1311544840L,101 ),
- (0 ,"NYC-0005","chris" ,"BT.L" ,1311544850L,102 ),
- (0 ,"NYC-0006","chris" ,"BT.L" ,1311544860L,103 )
+ (0 ,"NYC-0003","chris" ,"BT.L" ,1311544800030L,100 ),
+ (0 ,"NYC-0004","chris" ,"BT.L" ,1311544800040L,101 ),
+ (0 ,"NYC-0005","chris" ,"BT.L" ,1311544800050L,102 ),
+ (0 ,"NYC-0006","chris" ,"BT.L" ,1311544800060L,103 )
)
}
@@ -113,11 +118,11 @@ class VisualLinkedViewPortTest extends AbstractViewPortTestCase with Matchers wi
assertVpEqWithMeta(combineQs(viewPortOrders).filter(vpu => vpu.vp.id == viewPortOrders.id)) {
Table(
("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
- (0 ,"NYC-0007","chris" ,"BP.L" ,1311544870L,100 ),
- (0 ,"NYC-0008","chris" ,"BP.L" ,1311544880L,101 ),
- (0 ,"NYC-0009","chris" ,"BP.L" ,1311544890L,102 ),
- (0 ,"NYC-0010","chris" ,"BP.L" ,1311544900L,103 ),
- (0 ,"NYC-0011","chris" ,"BP.L" ,1311544910L,104 )
+ (0 ,"NYC-0007","chris" ,"BP.L" ,1311544800070L,100 ),
+ (0 ,"NYC-0008","chris" ,"BP.L" ,1311544800080L,101 ),
+ (0 ,"NYC-0009","chris" ,"BP.L" ,1311544800090L,102 ),
+ (0 ,"NYC-0010","chris" ,"BP.L" ,1311544800100L,103 ),
+ (0 ,"NYC-0011","chris" ,"BP.L" ,1311544800110L,104 )
)
}
@@ -140,11 +145,11 @@ class VisualLinkedViewPortTest extends AbstractViewPortTestCase with Matchers wi
Then("Check we still maintain the selection even with a new sort or filter")
assertVpEqWithMeta(combineQs(viewPortOrders).filter(vpu => vpu.vp.id == viewPortOrders.id)) {
Table(
- ("sel", "orderId", "trader", "ric", "tradeTime", "quantity"),
- (0, "NYC-0003", "chris", "BT.L", 1311544830L, 100),
- (0, "NYC-0004", "chris", "BT.L", 1311544840L, 101),
- (0, "NYC-0005", "chris", "BT.L", 1311544850L, 102),
- (0, "NYC-0006", "chris", "BT.L", 1311544860L, 103)
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ (0 ,"NYC-0003","chris" ,"BT.L" ,1311544800030L,100 ),
+ (0 ,"NYC-0004","chris" ,"BT.L" ,1311544800040L,101 ),
+ (0 ,"NYC-0005","chris" ,"BT.L" ,1311544800050L,102 ),
+ (0 ,"NYC-0006","chris" ,"BT.L" ,1311544800060L,103 )
)
}
@@ -161,20 +166,19 @@ class VisualLinkedViewPortTest extends AbstractViewPortTestCase with Matchers wi
assertVpEqWithMeta(orderUpdates2) {
Table(
- ("sel", "orderId", "trader", "ric", "tradeTime", "quantity"),
- (0, "NYC-0000", "chris", "VOD.L", 1311544800L, 100),
- (0, "NYC-0001", "chris", "VOD.L", 1311544810L, 101),
- (0, "NYC-0002", "chris", "VOD.L", 1311544820L, 102),
- (0, "NYC-0004", "chris", "BT.L", 1311544840L, 101),
- (0, "NYC-0005", "chris", "BT.L", 1311544850L, 102),
- (0, "NYC-0006", "chris", "BT.L", 1311544860L, 103),
- (0, "NYC-0008", "chris", "BP.L", 1311544880L, 101),
- (0, "NYC-0009", "chris", "BP.L", 1311544890L, 102),
- (0, "NYC-0010", "chris", "BP.L", 1311544900L, 103),
- (0, "NYC-0011", "chris", "BP.L", 1311544910L, 104)
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544800010L,101 ),
+ (0 ,"NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 ),
+ (0 ,"NYC-0004","chris" ,"BT.L" ,1311544800040L,101 ),
+ (0 ,"NYC-0005","chris" ,"BT.L" ,1311544800050L,102 ),
+ (0 ,"NYC-0006","chris" ,"BT.L" ,1311544800060L,103 ),
+ (0 ,"NYC-0008","chris" ,"BP.L" ,1311544800080L,101 ),
+ (0 ,"NYC-0009","chris" ,"BP.L" ,1311544800090L,102 ),
+ (0 ,"NYC-0010","chris" ,"BP.L" ,1311544800100L,103 ),
+ (0 ,"NYC-0011","chris" ,"BP.L" ,1311544800110L,104 )
)
}
-
}
}
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/disable/DisableViewPortTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/disable/DisableViewPortTest.scala
index 11d0369b6..b0543fe56 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/disable/DisableViewPortTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/disable/DisableViewPortTest.scala
@@ -1,16 +1,21 @@
package org.finos.vuu.viewport.disable
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper._
import org.finos.vuu.core.table.ViewPortColumnCreator
import org.finos.vuu.util.table.TableAsserts._
-import org.finos.vuu.viewport.{AbstractViewPortTestCase, ViewPortRange}
+import org.finos.vuu.viewport.{AbstractViewPortTestCase, TestTimeStamp, ViewPortRange}
import org.scalatest.GivenWhenThen
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.Tables.Table
class DisableViewPortTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen {
+ implicit val clock: Clock = new TestFriendlyClock(TestTimeStamp.EPOCH_DEFAULT)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+
Feature("Disable Viewport example") {
Scenario("Check that a viewport is not emptied when disabled") {
@@ -20,7 +25,7 @@ class DisableViewPortTest extends AbstractViewPortTestCase with Matchers with Gi
val viewPortColumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric"))
- createNOrderRowsNoSleep(ordersProvider, 10)(timeProvider)
+ createNOrderRowsNoSleep(ordersProvider, 10)(clock)
val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 10), viewPortColumns)
@@ -32,17 +37,17 @@ class DisableViewPortTest extends AbstractViewPortTestCase with Matchers with Gi
//it should return a compound error
assertVpEq(combinedUpdates) {
Table(
- ("orderId", "trader", "ric", "tradeTime", "quantity"),
- ("NYC-0000", "chris", "VOD.L", 1311544800L, 100),
- ("NYC-0001", "chris", "VOD.L", 1311544800L, 101),
- ("NYC-0002", "chris", "VOD.L", 1311544800L, 102),
- ("NYC-0003", "chris", "VOD.L", 1311544800L, 103),
- ("NYC-0004", "chris", "VOD.L", 1311544800L, 104),
- ("NYC-0005", "chris", "VOD.L", 1311544800L, 105),
- ("NYC-0006", "chris", "VOD.L", 1311544800L, 106),
- ("NYC-0007", "chris", "VOD.L", 1311544800L, 107),
- ("NYC-0008", "chris", "VOD.L", 1311544800L, 108),
- ("NYC-0009", "chris", "VOD.L", 1311544800L, 109)
+ ("orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ ("NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ ("NYC-0001","chris" ,"VOD.L" ,1311544800000L,101 ),
+ ("NYC-0002","chris" ,"VOD.L" ,1311544800000L,102 ),
+ ("NYC-0003","chris" ,"VOD.L" ,1311544800000L,103 ),
+ ("NYC-0004","chris" ,"VOD.L" ,1311544800000L,104 ),
+ ("NYC-0005","chris" ,"VOD.L" ,1311544800000L,105 ),
+ ("NYC-0006","chris" ,"VOD.L" ,1311544800000L,106 ),
+ ("NYC-0007","chris" ,"VOD.L" ,1311544800000L,107 ),
+ ("NYC-0008","chris" ,"VOD.L" ,1311544800000L,108 ),
+ ("NYC-0009","chris" ,"VOD.L" ,1311544800000L,109 )
)
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/editable/EditableViewPortTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/editable/EditableViewPortTest.scala
new file mode 100644
index 000000000..b3d3b3f72
--- /dev/null
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/editable/EditableViewPortTest.scala
@@ -0,0 +1,168 @@
+package org.finos.vuu.viewport.editable
+
+import org.finos.toolbox.jmx.MetricsProvider
+import org.finos.toolbox.lifecycle.LifecycleContainer
+import org.finos.toolbox.time.Clock
+import org.finos.vuu.api._
+import org.finos.vuu.core.VuuServer
+import org.finos.vuu.core.module.ModuleFactory.stringToString
+import org.finos.vuu.core.module.{StaticServedResource, TableDefContainer, ViewServerModule}
+import org.finos.vuu.core.table._
+import org.finos.vuu.net.ClientSessionId
+import org.finos.vuu.net.rest.RestService
+import org.finos.vuu.net.rpc.RpcHandler
+import org.finos.vuu.provider.{JoinTableProvider, JoinTableProviderImpl, MockProvider, Provider, ProviderContainer}
+import org.finos.vuu.util.OutboundRowPublishQueue
+import org.finos.vuu.viewport._
+import org.scalatest.GivenWhenThen
+import org.scalatest.matchers.should.Matchers
+
+abstract class EditableViewPortTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen {
+
+ final val TEST_TIME = 1450770869442L
+ //final val clock: Clock = new TestFriendlyClock(TEST_TIME)
+ //var counter: Int = 0
+
+ def createViewServerModule(theName: String): ViewServerModule = {
+ new ViewServerModule {
+ override def name: String = theName
+ override def tableDefContainer: TableDefContainer = ???
+ override def tableDefs: List[TableDef] = ???
+ override def serializationMixin: AnyRef = ???
+ override def rpcHandlersUnrealized: List[VuuServer => RpcHandler] = ???
+ override def getProviderForTable(table: DataTable, viewserver: VuuServer)(implicit time: Clock, lifecycleContainer: LifecycleContainer): Provider = ???
+ override def staticFileResources(): List[StaticServedResource] = ???
+ override def restServicesUnrealized: List[VuuServer => RestService] = ???
+ override def viewPortDefs: Map[String, (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef] = ???
+ }
+ }
+
+ def createRpcHandlerInstruments(mockProvider: MockProvider, tableContainer: TableContainer, clock: Clock): RpcHandler = {
+ new RpcHandler {
+
+ final val BASE_BASKET_TABLE = "basketOrders"
+
+ def createBasket(selection: ViewPortSelection, sessionId: ClientSessionId): ViewPortAction = {
+
+ val baseTable = tableContainer.getTable(BASE_BASKET_TABLE)
+
+ val sessionTable = tableContainer.createSimpleSessionTable(baseTable, sessionId)
+
+ val rows = selection.rowKeyIndex.keys.map(selection.viewPort.table.pullRow(_)).toList
+
+ rows.foreach(row => {
+
+ counter += 1
+ val ric = row.get("ric")
+ val currency = row.get("currency")
+ val exchange = row.get("exchange")
+ val clOrderId = "clOrderId-" + clock.now() + "-" + (counter)
+
+ val dataMap = Map(
+ "clientOrderId" -> clOrderId,
+ "ric" -> ric,
+ "currency" -> currency,
+ "lastModifiedTime" -> clock.now(),
+ "exchange" -> exchange
+ )
+
+ sessionTable.processUpdate(clOrderId, RowWithData(clOrderId, dataMap), clock.now())
+ })
+
+ OpenDialogViewPortAction(ViewPortTable(sessionTable.name, baseTable.getTableDef.getModule().name))
+ }
+
+ override def menuItems(): ViewPortMenu = ViewPortMenu("Test Menu",
+ new SelectionViewPortMenuItem("Create Basket", "", this.createBasket, "CREATE_BASKET")
+ )
+ }
+ }
+
+ //final val TEST_TIME = 1450770869442L
+ var counter: Int = 0
+
+ def setupEditableTableInfra()(implicit clock: Clock, metrics: MetricsProvider, lifecycle: LifecycleContainer): (ViewPortContainer, Map[String, (DataTable, MockProvider)], ClientSessionId, OutboundRowPublishQueue, TableContainer, JoinTableProvider) = {
+
+ val module = createViewServerModule("TEST")
+
+ val constituentDef = TableDef(
+ name = "constituent",
+ keyField = "id",
+ columns = Columns.fromNames("id".string(), "ric".string(), "quantity".long()),
+ VisualLinks(),
+ joinFields = "id", "ric"
+ )
+
+ val instrumentDef = TableDef(
+ name = "instrument",
+ keyField = "ric",
+ columns = Columns.fromNames("ric:String", "description:String"),
+ joinFields = "ric"
+ )
+
+ val pricesDef = TableDef(
+ name = "price",
+ keyField = "ric",
+ columns = Columns.fromNames("ric:String", "bid:Long", "ask:Long"),
+ joinFields = "ric"
+ )
+
+ val joinDef = JoinTableDef(
+ name = "consInstrumentPrice",
+ baseTable = constituentDef,
+ joinColumns = Columns.allFrom(constituentDef) ++ Columns.allFromExcept(instrumentDef, "ric") ++ Columns.allFromExcept(pricesDef, "ric"),
+ joinFields = List("ric"),
+ joins =
+ JoinTo(
+ table = instrumentDef,
+ joinSpec = JoinSpec(left = "ric", right = "ric", LeftOuterJoin)
+ ),
+ JoinTo(
+ table = pricesDef,
+ joinSpec = JoinSpec(left = "ric", right = "ric", LeftOuterJoin)
+ )
+ )
+
+ constituentDef.setModule(module)
+ instrumentDef.setModule(module)
+ pricesDef.setModule(module)
+ joinDef.setModule(module)
+
+ val joinProvider = JoinTableProviderImpl()
+
+ val tableContainer = new TableContainer(joinProvider)
+
+ val constituent = tableContainer.createTable(constituentDef)
+ val instrument = tableContainer.createTable(instrumentDef)
+ val prices = tableContainer.createTable(pricesDef)
+
+ val consInstrumentPrices = tableContainer.createJoinTable(joinDef)
+
+ val constituentProvider = new MockProvider(constituent)
+ val instrumentProvider = new MockProvider(instrument)
+ val pricesProvider = new MockProvider(prices)
+
+ val providerContainer = new ProviderContainer(joinProvider)
+ val viewPortContainer = setupViewPort(tableContainer, providerContainer)
+
+ joinProvider.start()
+ joinProvider.runOnce()
+
+ val session = ClientSessionId("sess-01", "chris")
+
+ val outQueue = new OutboundRowPublishQueue()
+
+ val mapTables = Map(constituent.name -> (constituent, constituentProvider),
+ instrument.name -> (instrument, instrumentProvider),
+ prices.name -> (prices, pricesProvider),
+ consInstrumentPrices.name -> (consInstrumentPrices, null))
+
+ (viewPortContainer, mapTables, session, outQueue, tableContainer, joinProvider)
+ }
+
+ def createViewPortDefFunc(tableContainer: TableContainer, rpcHandler: RpcHandler, clock: Clock): (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef = {
+ val func = (t: DataTable, provider: Provider, pc: ProviderContainer, table: TableContainer) => ViewPortDef(t.getTableDef.columns, rpcHandler)
+ func
+ }
+
+}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/editable/EditableViewportWithRpcTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/editable/EditableViewportWithRpcTest.scala
new file mode 100644
index 000000000..5996a3702
--- /dev/null
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/editable/EditableViewportWithRpcTest.scala
@@ -0,0 +1,148 @@
+package org.finos.vuu.viewport.editable
+
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.lifecycle.LifecycleContainer
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
+import org.finos.vuu.api.JoinTableDef
+import org.finos.vuu.client.messages.RequestId
+import org.finos.vuu.core.table.TableTestHelper.combineQs
+import org.finos.vuu.core.table._
+import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler}
+import org.finos.vuu.net.{ClientSessionId, RequestContext}
+import org.finos.vuu.util.table.TableAsserts.assertVpEq
+import org.finos.vuu.viewport._
+import org.scalatest.prop.Tables.Table
+
+class ConstituentInstrumentPricesRpcService(val tableContainer: TableContainer)(implicit clock: Clock) extends RpcHandler with EditRpcHandler{
+
+ private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
+ val joinTable = vp.table.asTable.asInstanceOf[JoinTable]
+ val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable
+ joinTable.sourceTables.get(baseTableDef.name) match {
+ case Some(table: DataTable) =>
+ table.processUpdate(key, RowWithData(key, Map("id" -> key, columnName -> data)), clock.now())
+ ViewPortEditSuccess()
+ case None =>
+ ViewPortEditFailure("Could not find base table")
+ }
+ }
+
+ def sendToMarket()(ctx: RequestContext): ViewPortAction = {
+ logger.info("Calling sendToMarket()")
+ ViewPortEditSuccess()
+ }
+ def createBasket(name: String)(ctx: RequestContext): ViewPortAction = {
+ logger.info("Calling createBasket()")
+ ViewPortEditSuccess()
+ }
+
+ private def onEditRow(key: String, row: Map[String, Any], vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
+ val table = vp.table.asTable
+ table.processUpdate(key, RowWithData(key, row), clock.now())
+ ViewPortEditSuccess()
+ }
+
+ private def onFormSubmit(vp: ViewPort, session: ClientSessionId): ViewPortAction = {
+ val table = vp.table.asTable
+ val primaryKeys = table.primaryKeys
+ val headKey = primaryKeys.head
+ val sequencerNumber = table.pullRow(headKey).get("sequenceNumber").asInstanceOf[Long]
+
+ if (sequencerNumber > 0) {
+ logger.info("I would now send this fix seq to a fix engine to reset, we're all good:" + sequencerNumber)
+ CloseDialogViewPortAction(vp.id)
+ } else {
+ logger.error("Seq number not set, returning error")
+ ViewPortEditFailure("Sequencer number has not been set.")
+ }
+ }
+
+ private def onFormClose(vp: ViewPort, session: ClientSessionId): ViewPortAction = {
+ CloseDialogViewPortAction(vp.id)
+ }
+
+ override def editCellAction(): ViewPortEditCellAction = ViewPortEditCellAction("", this.onEditCell)
+ override def editRowAction(): ViewPortEditRowAction = ViewPortEditRowAction("", this.onEditRow)
+ override def onFormSubmit(): ViewPortFormSubmitAction = ViewPortFormSubmitAction("", this.onFormSubmit)
+ override def deleteRowAction(): ViewPortDeleteRowAction = ViewPortDeleteRowAction("", (x, y, z) => ViewPortEditSuccess())
+ override def deleteCellAction(): ViewPortDeleteCellAction = ViewPortDeleteCellAction("", (a, b, c, d) => ViewPortEditSuccess())
+ override def addRowAction(): ViewPortAddRowAction = ViewPortAddRowAction("", (a, b, c, d) => ViewPortEditSuccess())
+ override def onFormClose(): ViewPortFormCloseAction = ViewPortFormCloseAction("", this.onFormClose)
+}
+
+class EditableViewportWithRpcTest extends EditableViewPortTest {
+
+ Feature("Test full flow through editable session table") {
+
+ Scenario("Create a session table based on a user action and populate it with callbacks") {
+
+ implicit val clock: Clock = new TestFriendlyClock(TEST_TIME)
+ implicit val lifecycle: LifecycleContainer = new LifecycleContainer
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+
+ val (viewPortContainer, tablesAndProviders, session, outQueue, tableContainer, joinTableManager) = setupEditableTableInfra()
+ val context = RequestContext("AAA", session, outQueue, "AAABBBCC")
+
+ val (constituent, consProvider) = tablesAndProviders("constituent")
+ val (instrument, instrumentProvider) = tablesAndProviders("instrument")
+ val (prices, pricesProvider) = tablesAndProviders("price")
+ val (consInstPrice, _) = tablesAndProviders("consInstrumentPrice")
+
+ Given("We define a viewport callback on process with an rpc service attached...")
+ viewPortContainer.addViewPortDefinition(consInstPrice.getTableDef.name, createViewPortDefFunc(tableContainer, new ConstituentInstrumentPricesRpcService(tableContainer), clock))
+
+ And("we've ticked in some data")
+ consProvider.tick("bskt1.vod.l", Map("id" -> "bskt1.vod.l", "ric" -> "VOD.L", "quantity" -> 1000L))
+ consProvider.tick("bskt1.bt.l", Map("id" -> "bskt1.bt.l", "ric" -> "BT.L", "quantity" -> 500L))
+
+ instrumentProvider.tick("VOD.L", Map("ric" -> "VOD.L", "description" -> "Vodafone"))
+ instrumentProvider.tick("BT.L", Map("ric" -> "BT.L", "description" -> "British Telecom"))
+
+ pricesProvider.tick("VOD.L", Map("ric" -> "VOD.L", "bid" -> 123L, "ask" -> 124L))
+ pricesProvider.tick("BT.L", Map("ric" -> "BT.L", "bid" -> 223L, "ask" -> 224L))
+
+ joinTableManager.runOnce()
+
+ Given("We create a viewport on the consInstPrice table (view only)")
+ val vpColumns = ViewPortColumnCreator.create(consInstPrice, consInstPrice.getTableDef.columns.map(_.name).toList)
+ val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, consInstPrice, DefaultRange, vpColumns)
+
+ joinTableManager.runOnce()
+ viewPortContainer.runOnce()
+
+ Then("we can expect the result of the join...")
+ assertVpEq(combineQs(viewPort)) {
+ Table(
+ ("id" ,"ric" ,"quantity","description","bid" ,"ask" ),
+ ("bskt1.bt.l","BT.L" ,500L ,"British Telecom",223L ,224L ),
+ ("bskt1.vod.l","VOD.L" ,1000L ,"Vodafone",123L ,124L )
+ )
+ }
+
+ Then("we call and edit rpc call")
+ viewPortContainer.callRpcEditCell(viewPort.id, "bskt1.vod.l", "quantity", Long.box(2000L), session)
+ viewPortContainer.callRpcEditCell(viewPort.id, "bskt1.bt.l", "quantity", Long.box(600L), session)
+
+ joinTableManager.runOnce()
+ viewPortContainer.runOnce()
+
+ And("we can see the result of the rpc on the join table")
+ assertVpEq(combineQs(viewPort)) {
+ Table(
+ ("id" ,"ric" ,"quantity","description","bid" ,"ask" ),
+ ("bskt1.bt.l","BT.L" ,600L ,"British Telecom",223L ,224L ),
+ ("bskt1.vod.l","VOD.L" ,2000L ,"Vodafone",123L ,124L )
+ )
+ }
+
+ When("we call a viewport specific rpc call (sendToMarket)")
+ viewPortContainer.callRpcService(viewPort.id, "sendToMarket", Array(), Map(), session)(context) match {
+ case action: ViewPortAction =>
+ println(action)
+ action.getClass shouldEqual classOf[ViewPortEditSuccess]
+ case _ =>
+ fail("Should not be here in test, means call has failed")
+ }
+ }
+ }
+}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/performance/DontRecalculateUnchangedViewPortTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/performance/DontRecalculateUnchangedViewPortTest.scala
index a6ff14a45..7c6f434aa 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/performance/DontRecalculateUnchangedViewPortTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/performance/DontRecalculateUnchangedViewPortTest.scala
@@ -1,16 +1,21 @@
package org.finos.vuu.viewport.performance
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper.combineQs
import org.finos.vuu.core.table.ViewPortColumnCreator
import org.finos.vuu.util.table.TableAsserts.assertVpEqWithMeta
-import org.finos.vuu.viewport.{AbstractViewPortTestCase, ViewPortRange}
+import org.finos.vuu.viewport.{AbstractViewPortTestCase, TestTimeStamp, ViewPortRange}
import org.scalatest.GivenWhenThen
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.Tables.Table
class DontRecalculateUnchangedViewPortTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen {
+ implicit val clock: Clock = new TestFriendlyClock(TestTimeStamp.EPOCH_DEFAULT)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+
Feature("Dont recalculate viewport when hasnt changed") {
Scenario("Create Viewport, run cycle count and check we recalc only when required") {
@@ -20,7 +25,7 @@ class DontRecalculateUnchangedViewPortTest extends AbstractViewPortTestCase with
val vpcolumns = ViewPortColumnCreator.create(orders, List("orderId", "trader", "tradeTime", "quantity", "ric"))
- createNOrderRows(ordersProvider, 10)(timeProvider)
+ createNOrderRows(ordersProvider, 10)(clock)
val viewPort = viewPortContainer.create(RequestId.oneNew(), session, outQueue, orders, ViewPortRange(0, 20), vpcolumns)
@@ -31,17 +36,17 @@ class DontRecalculateUnchangedViewPortTest extends AbstractViewPortTestCase with
assertVpEqWithMeta(combineQs(viewPort)) {
Table(
- ("sel", "orderId", "trader", "ric", "tradeTime", "quantity"),
- (0, "NYC-0000", "chris", "VOD.L", 1311544800L, 100),
- (0, "NYC-0001", "chris", "VOD.L", 1311544810L, 101),
- (0, "NYC-0002", "chris", "VOD.L", 1311544820L, 102),
- (0, "NYC-0003", "chris", "VOD.L", 1311544830L, 103),
- (0, "NYC-0004", "chris", "VOD.L", 1311544840L, 104),
- (0, "NYC-0005", "chris", "VOD.L", 1311544850L, 105),
- (0, "NYC-0006", "chris", "VOD.L", 1311544860L, 106),
- (0, "NYC-0007", "chris", "VOD.L", 1311544870L, 107),
- (0, "NYC-0008", "chris", "VOD.L", 1311544880L, 108),
- (0, "NYC-0009", "chris", "VOD.L", 1311544890L, 109)
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime","quantity"),
+ (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800000L,100 ),
+ (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544800010L,101 ),
+ (0 ,"NYC-0002","chris" ,"VOD.L" ,1311544800020L,102 ),
+ (0 ,"NYC-0003","chris" ,"VOD.L" ,1311544800030L,103 ),
+ (0 ,"NYC-0004","chris" ,"VOD.L" ,1311544800040L,104 ),
+ (0 ,"NYC-0005","chris" ,"VOD.L" ,1311544800050L,105 ),
+ (0 ,"NYC-0006","chris" ,"VOD.L" ,1311544800060L,106 ),
+ (0 ,"NYC-0007","chris" ,"VOD.L" ,1311544800070L,107 ),
+ (0 ,"NYC-0008","chris" ,"VOD.L" ,1311544800080L,108 ),
+ (0 ,"NYC-0009","chris" ,"VOD.L" ,1311544800090L,109 )
)
}
@@ -94,21 +99,21 @@ class DontRecalculateUnchangedViewPortTest extends AbstractViewPortTestCase with
assertVpEqWithMeta(combineQs(viewPort)) {
Table(
- ("sel", "orderId", "trader", "ric", "tradeTime"),
- (0, "NYC-0000", "chris", "VOD.L", 1311544800L),
- (0, "NYC-0001", "chris", "VOD.L", 1311544810L),
- (0, "NYC-0002", "chris", "VOD.L", 1311544820L),
- (0, "NYC-0003", "chris", "VOD.L", 1311544830L),
- (0, "NYC-0004", "chris", "VOD.L", 1311544840L),
- (0, "NYC-0005", "chris", "VOD.L", 1311544850L),
- (0, "NYC-0006", "chris", "VOD.L", 1311544860L),
- (0, "NYC-0007", "chris", "VOD.L", 1311544870L),
- (0, "NYC-0008", "chris", "VOD.L", 1311544880L),
- (0, "NYC-0009", "chris", "VOD.L", 1311544890L)
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime"),
+ (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800000L),
+ (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544800010L),
+ (0 ,"NYC-0002","chris" ,"VOD.L" ,1311544800020L),
+ (0 ,"NYC-0003","chris" ,"VOD.L" ,1311544800030L),
+ (0 ,"NYC-0004","chris" ,"VOD.L" ,1311544800040L),
+ (0 ,"NYC-0005","chris" ,"VOD.L" ,1311544800050L),
+ (0 ,"NYC-0006","chris" ,"VOD.L" ,1311544800060L),
+ (0 ,"NYC-0007","chris" ,"VOD.L" ,1311544800070L),
+ (0 ,"NYC-0008","chris" ,"VOD.L" ,1311544800080L),
+ (0 ,"NYC-0009","chris" ,"VOD.L" ,1311544800090L)
)
}
- createNOrderRows(ordersProvider, 12)(timeProvider)
+ createNOrderRows(ordersProvider, 12)(clock)
viewPortContainer.shouldCalculateKeys(viewPort2, viewPort2.getStructuralHashCode(), viewPort2.getTableUpdateCount()) should be(true)
@@ -116,22 +121,21 @@ class DontRecalculateUnchangedViewPortTest extends AbstractViewPortTestCase with
assertVpEqWithMeta(combineQs(viewPort)) {
Table(
- ("sel", "orderId", "trader", "ric", "tradeTime"),
- (0, "NYC-0010", "chris", "VOD.L", 1311545000L),
- (0, "NYC-0011", "chris", "VOD.L", 1311545010L),
- (0, "NYC-0000", "chris", "VOD.L", 1311544900L),
- (0, "NYC-0001", "chris", "VOD.L", 1311544910L),
- (0, "NYC-0002", "chris", "VOD.L", 1311544920L),
- (0, "NYC-0003", "chris", "VOD.L", 1311544930L),
- (0, "NYC-0004", "chris", "VOD.L", 1311544940L),
- (0, "NYC-0005", "chris", "VOD.L", 1311544950L),
- (0, "NYC-0006", "chris", "VOD.L", 1311544960L),
- (0, "NYC-0007", "chris", "VOD.L", 1311544970L),
- (0, "NYC-0008", "chris", "VOD.L", 1311544980L),
- (0, "NYC-0009", "chris", "VOD.L", 1311544990L)
+ ("sel" ,"orderId" ,"trader" ,"ric" ,"tradeTime"),
+ (0 ,"NYC-0010","chris" ,"VOD.L" ,1311544800200L),
+ (0 ,"NYC-0011","chris" ,"VOD.L" ,1311544800210L),
+ (0 ,"NYC-0000","chris" ,"VOD.L" ,1311544800100L),
+ (0 ,"NYC-0001","chris" ,"VOD.L" ,1311544800110L),
+ (0 ,"NYC-0002","chris" ,"VOD.L" ,1311544800120L),
+ (0 ,"NYC-0003","chris" ,"VOD.L" ,1311544800130L),
+ (0 ,"NYC-0004","chris" ,"VOD.L" ,1311544800140L),
+ (0 ,"NYC-0005","chris" ,"VOD.L" ,1311544800150L),
+ (0 ,"NYC-0006","chris" ,"VOD.L" ,1311544800160L),
+ (0 ,"NYC-0007","chris" ,"VOD.L" ,1311544800170L),
+ (0 ,"NYC-0008","chris" ,"VOD.L" ,1311544800180L),
+ (0 ,"NYC-0009","chris" ,"VOD.L" ,1311544800190L)
)
}
-
}
}
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/sessiontable/EditSessionTableTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/sessiontable/EditSessionTableTest.scala
index d216085b2..93d73454e 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/sessiontable/EditSessionTableTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/sessiontable/EditSessionTableTest.scala
@@ -1,5 +1,6 @@
package org.finos.vuu.viewport.sessiontable
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.client.messages.RequestId
import org.finos.vuu.core.table.TableTestHelper.{combineQs, emptyQueues}
@@ -15,7 +16,7 @@ import org.scalatest.prop.Tables.Table
class EditSessionTableTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen with AbstractSessionTestCase {
final implicit val clock: Clock = new TestFriendlyClock(TEST_TIME)
- //implicit val metrics: MetricsProvider = new MetricsProviderImpl
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
/**
* This is the process rpc service.
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/sessiontable/SessionTableViewportTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/sessiontable/SessionTableViewportTest.scala
index f9b8cc669..26e5c95d6 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/sessiontable/SessionTableViewportTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/sessiontable/SessionTableViewportTest.scala
@@ -1,5 +1,6 @@
package org.finos.vuu.viewport.sessiontable
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
import org.finos.toolbox.lifecycle.LifecycleContainer
import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.api._
@@ -24,7 +25,6 @@ import org.scalatest.prop.Tables.Table
class SessionTableViewportTest extends AbstractViewPortTestCase with Matchers with GivenWhenThen {
final val TEST_TIME = 1450770869442L
- final val clock: Clock = new TestFriendlyClock(TEST_TIME)
var counter: Int = 0
def createViewServerModule(theName: String): ViewServerModule = {
@@ -82,8 +82,8 @@ class SessionTableViewportTest extends AbstractViewPortTestCase with Matchers wi
}
}
- def createDefaultSessionTableInfra(): (ViewPortContainer, DataTable, MockProvider, DataTable, MockProvider, ClientSessionId, OutboundRowPublishQueue, DataTable, TableContainer) = {
- implicit val lifecycle = new LifecycleContainer
+ def createDefaultSessionTableInfra()(implicit clock: Clock, lifecycle:LifecycleContainer, metrics: MetricsProvider): (ViewPortContainer, DataTable, MockProvider, DataTable, MockProvider, ClientSessionId, OutboundRowPublishQueue, DataTable, TableContainer) = {
+ //implicit val lifecycle = new LifecycleContainer
val dateTime = 1437728400000L //new LocalDateTime(2015, 7, 24, 11, 0).toDateTime.toInstant.getMillis
@@ -185,6 +185,10 @@ class SessionTableViewportTest extends AbstractViewPortTestCase with Matchers wi
Scenario("Create a session table from an rpc call and edit it") {
+ implicit val clock: Clock = new TestFriendlyClock(1311544800)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+ implicit val lifecycle: LifecycleContainer = new LifecycleContainer
+
val (viewPortContainer, instruments, instrumentsProvider, prices, pricesProvider, session, outQueue, basketOrders, tableContainer) = createDefaultSessionTableInfra()
val vpcolumns = ViewPortColumnCreator.create(instruments, instruments.getTableDef.columns.map(_.name).toList)
@@ -236,8 +240,8 @@ class SessionTableViewportTest extends AbstractViewPortTestCase with Matchers wi
Then("verify the table is populated")
assertVpEq(combinedUpdates2) {
Table(
- ("ric", "clientOrderId", "currency", "lastModifiedTime", "quantity", "price", "priceType", "exchange", "orderId", "effectivePrice"),
- ("VOD.L", "clOrderId-1450770869442-1", "GBp", 1450770869442L, null, null, null, "XLON/SETS", null, null)
+ ("ric" ,"clientOrderId","currency","lastModifiedTime","quantity","price" ,"priceType","exchange","orderId" ,"effectivePrice"),
+ ("VOD.L" ,"clOrderId-1311544800-1","GBp" ,1311544800L,null ,null ,null ,"XLON/SETS",null ,null )
)
}
}
diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/validation/CreateValidViewportTest.scala b/vuu/src/test/scala/org/finos/vuu/viewport/validation/CreateValidViewportTest.scala
index b9daedc70..be0d6ac6f 100644
--- a/vuu/src/test/scala/org/finos/vuu/viewport/validation/CreateValidViewportTest.scala
+++ b/vuu/src/test/scala/org/finos/vuu/viewport/validation/CreateValidViewportTest.scala
@@ -1,5 +1,8 @@
package org.finos.vuu.viewport.validation
+import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl}
+import org.finos.toolbox.lifecycle.LifecycleContainer
+import org.finos.toolbox.time.{Clock, TestFriendlyClock}
import org.finos.vuu.core.CoreServerApiHandler
import org.finos.vuu.net.{ClientSessionId, CreateViewPortRequest, RequestContext}
import org.finos.vuu.viewport.{AbstractViewPortTestCase, ViewPortRange, ViewPortTable}
@@ -12,6 +15,10 @@ class CreateValidViewportTest extends AbstractViewPortTestCase with Matchers wit
Scenario("create viewport, with incorrect columns, check for error") {
+ implicit val clock: Clock = new TestFriendlyClock(1311544800)
+ implicit val metrics: MetricsProvider = new MetricsProviderImpl
+ implicit val lifecycle: LifecycleContainer = new LifecycleContainer
+
Given("we've created a viewport with orders in")
val (viewPortContainer, _, _, prices, _, _, outQueue) = createDefaultOrderPricesViewPortInfra()