Skip to content

Commit

Permalink
finos#1073 enable date/time pattern selection through ColumnSettings
Browse files Browse the repository at this point in the history
- converges date and time column types to one unified date/time type, consistent
  with how some major programming languages handles datetime.
- date/time pattern selection is only available for columns with "date/time" type.
- changes DateTimePattern to be an object instead of strings to enable
  simultaneous selection of both date and time patterns.
- also moves DateTimeColumnDescriptor to vuu-table-types package for consistency.
  • Loading branch information
junaidzm13 committed Jan 2, 2024
1 parent 733068a commit 891fcc1
Show file tree
Hide file tree
Showing 23 changed files with 341 additions and 107 deletions.
13 changes: 7 additions & 6 deletions vuu-ui/cypress/pages/ShellWithNewTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ import { SHELL_WITH_NEW_THEME_URL } from "../support/e2e/constants";
export class ShellWithNewTheme {
visit: () => void = () => {
cy.visit(SHELL_WITH_NEW_THEME_URL);
}
};

getContextMenuButton: () => Cypress.Chainable<JQuery<HTMLElement>> = () => {
return cy
.findByRole("tablist", { name: "data tabs" })
.findAllByRole("tab")
.first()
.findByRole("button", { name: "context menu" });
}
};

getSaveLayoutButton: () => Cypress.Chainable<JQuery<HTMLElement>> = () => {
return cy.findByRole("menuitem", { name: "Save Layout" });
}
};

getMyLayoutsButton: () => Cypress.Chainable<JQuery<HTMLElement>> = () => {
return cy.findByRole("tab", { name: "MY LAYOUTS" });
}
};

getLayoutTile: (
layoutName: string,
Expand All @@ -33,12 +33,13 @@ export class ShellWithNewTheme {
creator: string,
date: Date
) => {
const layoutTileName = `${layoutName} ${creator}, ${formatDate(date)}`;
const formattedDate = formatDate({ date: "dd.mm.yyyy" })(date);
const layoutTileName = `${layoutName} ${creator}, ${formattedDate}`;

return cy
.findByRole("listbox", { name: "my layouts" })
.findByRole("list", { name: group })
.findByRole("listitem", { name: layoutTileName })
.findByRole("button");
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class LocalPersistenceManager implements PersistenceManager {
const newMetadata: LayoutMetadata = {
...metadata,
id,
created: formatDate("dd.mm.yyyy")(new Date()),
created: formatDate({ date: "dd.mm.yyyy" })(new Date()),
};

this.saveLayoutsWithMetadata(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const persistenceManager = new LocalPersistenceManager();

const existingId = "existing_id";

const newDate = formatDate("dd.mm.yyyy")(new Date());
const newDate = formatDate({ date: "dd.mm.yyyy" })(new Date());

const existingMetadata: LayoutMetadata = {
id: existingId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import {
getCellRendererOptions,
getConfigurationEditor,
isColumnTypeRenderer,
isDateTimeColumn,
isTypeDescriptor,
} from "@finos/vuu-utils";
import { FormField, FormFieldLabel } from "@salt-ds/core";
import cx from "clsx";
import { HTMLAttributes, useCallback, useMemo } from "react";
import { NumericFormattingSettings } from "./NumericFormattingSettings";
import { DateTimeFormattingSettings } from "./DateTimeFormattingSettings";
import { FormattingSettingsProps } from "./types";

const classBase = "vuuColumnFormattingPanel";

Expand All @@ -38,21 +41,10 @@ export const ColumnFormattingPanel = ({
onChangeRendering,
...htmlAttributes
}: ColumnFormattingPanelProps) => {
const contentForType = useMemo(() => {
switch (column.serverDataType) {
case "double":
case "int":
case "long":
return (
<NumericFormattingSettings
column={column}
onChange={onChangeFormatting}
/>
);
default:
return null;
}
}, [column, onChangeFormatting]);
const formattingSettingsForType = useMemo(
() => formattingSettingsByColType({ column, onChange: onChangeFormatting }),
[column, onChangeFormatting]
);

const ConfigEditor = useMemo<
React.FC<ConfigurationEditorProps> | undefined
Expand Down Expand Up @@ -81,7 +73,7 @@ export const ColumnFormattingPanel = ({
const handleChangeRenderer = useCallback<
SingleSelectionHandler<CellRendererDescriptor>
>(
(evt, cellRendererDescriptor) => {
(_, cellRendererDescriptor) => {
const renderProps: ColumnTypeRendering = {
name: cellRendererDescriptor.name,
};
Expand Down Expand Up @@ -112,7 +104,7 @@ export const ColumnFormattingPanel = ({
<div
className={cx(classBase, className, `${classBase}-${serverDataType}`)}
>
{contentForType}
{formattingSettingsForType}
{ConfigEditor ? (
<ConfigEditor
column={column as ColumnDescriptorCustomRenderer}
Expand All @@ -123,3 +115,20 @@ export const ColumnFormattingPanel = ({
</div>
);
};

function formattingSettingsByColType(props: FormattingSettingsProps) {
const { column } = props;

if (isDateTimeColumn(column)) {
return <DateTimeFormattingSettings {...props} column={column} />;
}

switch (column.serverDataType) {
case "double":
case "int":
case "long":
return <NumericFormattingSettings {...props} />;
default:
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.vuuDateTimeFormattingSettings {
display: flex;
flex-direction: column;
gap: 4px;
padding-top: 6px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React, { SyntheticEvent, useCallback, useMemo, useState } from "react";
import { Dropdown, SingleSelectionHandler } from "@finos/vuu-ui-controls";
import {
DateTimePattern,
defaultPatternsByType,
fallbackDateTimePattern,
getTypeFormattingFromColumn,
supportedDateTimePatterns,
} from "@finos/vuu-utils";
import {
FormField,
FormFieldLabel,
ToggleButton,
ToggleButtonGroup,
} from "@salt-ds/core";
import { DateTimeColumnDescriptor } from "@finos/vuu-table-types";
import { FormattingSettingsProps } from "./types";

import "./DateTimeFormattingSettings.css";

const classBase = "vuuDateTimeFormattingSettings";

export const DateTimeFormattingSettings: React.FC<
FormattingSettingsProps<DateTimeColumnDescriptor>
> = (props) => {
const { column, onChange } = props;
const formatting = getTypeFormattingFromColumn(column);
const { pattern = fallbackDateTimePattern } = formatting;
const toggleValue = useMemo(() => getToggleValue(pattern), [pattern]);

const [fallbackState, setFallbackState] = useState<Required<DateTimePattern>>(
{
time: pattern.time ?? defaultPatternsByType.time,
date: pattern.date ?? defaultPatternsByType.date,
}
);

const onPatternChange = useCallback(
(pattern: DateTimePattern) => onChange({ ...formatting, pattern }),
[onChange, formatting]
);

const onDropdownChange = useCallback<
<T extends keyof DateTimePattern>(
key: T
) => SingleSelectionHandler<Required<DateTimePattern>[T]>
>(
(key) => (_, p) => {
const updatedPattern = { ...(pattern ?? {}), [key]: p };
setFallbackState((s) => ({
time: updatedPattern.time ?? s.time,
date: updatedPattern.date ?? s.date,
}));
onPatternChange(updatedPattern);
},
[onPatternChange, pattern]
);

const onToggleChange = useCallback(
(evnt: SyntheticEvent<HTMLButtonElement, Event>) => {
const value = evnt.currentTarget.value as ToggleValue;
switch (value) {
case "time":
return onPatternChange({
[value]: pattern[value] ?? fallbackState[value],
});
case "date":
return onPatternChange({
[value]: pattern[value] ?? fallbackState[value],
});
case "both":
return onPatternChange({
time: pattern.time ?? fallbackState.time,
date: pattern.date ?? fallbackState.date,
});
}
},
[onPatternChange]
);

return (
<div className={classBase}>
<FormField>
<FormFieldLabel>{"Date/Time pattern"}</FormFieldLabel>
<ToggleButtonGroup
className="vuuToggleButtonGroup"
onChange={onToggleChange}
value={toggleValue}
>
{toggleValues.map((v) => (
<ToggleButton key={v} value={v}>
{v.toUpperCase()}
</ToggleButton>
))}
</ToggleButtonGroup>
</FormField>

{(["date", "time"] as const)
.filter((v) => !!pattern[v])
.map((v) => (
<FormField labelPlacement="left" key={v}>
<FormFieldLabel>{`${labelByType[v]}`}</FormFieldLabel>
<Dropdown<Required<DateTimePattern>[typeof v]>
onSelectionChange={onDropdownChange(v)}
selected={pattern[v]}
source={supportedDateTimePatterns[v]}
width="100%"
/>
</FormField>
))}
</div>
);
};

const labelByType: Record<keyof DateTimePattern, string> = {
date: "Date",
time: "Time",
};

const toggleValues = ["date", "time", "both"] as const;

type ToggleValue = (typeof toggleValues)[number];

function getToggleValue(pattern: DateTimePattern): ToggleValue {
return !pattern.time ? "date" : !pattern.date ? "time" : "both";
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,11 @@ import {
useCallback,
useState,
} from "react";
import { FormattingSettingsProps } from "./types";

const classBase = "vuuFormattingSettings";

export interface NumericFormattingSettingsProps {
column: ColumnDescriptor;
onChange: (formatting: ColumnTypeFormatting) => void;
}

export const NumericFormattingSettings = ({
column,
onChange,
}: NumericFormattingSettingsProps) => {
export const NumericFormattingSettings = ({column, onChange}: FormattingSettingsProps) => {
const [formattingSettings, setFormattingSettings] =
useState<ColumnTypeFormatting>(getTypeFormattingFromColumn(column));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./ColumnFormattingPanel";
export * from "./NumericFormattingSettings";
export * from "./DateTimeFormattingSettings";
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ColumnDescriptor, ColumnTypeFormatting } from "@finos/vuu-table-types";

export interface FormattingSettingsProps<T extends ColumnDescriptor = ColumnDescriptor> {
column: T;
onChange: (formatting: ColumnTypeFormatting) => void;
}
21 changes: 17 additions & 4 deletions vuu-ui/packages/vuu-table-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
import type { VuuDataRow } from "@finos/vuu-protocol-types";
import type { ValueFormatter } from "@finos/vuu-table";
import type { ClientSideValidationChecker } from "@finos/vuu-ui-controls";
import type { DateTimePattern } from "@finos/vuu-utils";
import type { FunctionComponent, MouseEvent } from "react";
import type { HTMLAttributes } from "react";

Expand Down Expand Up @@ -77,7 +78,7 @@ export interface GridConfig extends TableConfig {
export declare type ColumnTypeFormatting = {
alignOnDecimals?: boolean;
decimals?: number;
pattern?: string;
pattern?: DateTimePattern;
zeroPad?: boolean;
};

Expand Down Expand Up @@ -128,7 +129,19 @@ export interface ValueListRenderer {
values: string[];
}

export declare type DateTimeColumnTypeSimple = "date" | "time";
export declare type DateTimeColumnTypeSimple = "date/time";

type DateTimeColumnType =
| DateTimeColumnTypeSimple
| (Omit<ColumnTypeDescriptor, "name"> & { name: DateTimeColumnTypeSimple });

export declare type DateTimeColumnDescriptor = Omit<
ColumnDescriptor,
"type"
> & {
type: DateTimeColumnType;
};

export declare type ColumnTypeSimple =
| "string"
| "number"
Expand Down Expand Up @@ -180,9 +193,9 @@ export interface ColumnDescriptor {
colHeaderLabelRenderer?: string;
editable?: boolean;
flex?: number;
/**
/**
Optional additional level(s) of heading to display above label.
May span multiple columns, if multiple adjacent columns declare
May span multiple columns, if multiple adjacent columns declare
same heading at same level.
*/
heading?: string[];
Expand Down
17 changes: 3 additions & 14 deletions vuu-ui/packages/vuu-utils/src/column-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import type {
ColumnTypeFormatting,
ColumnTypeRendering,
ColumnTypeWithValidationRules,
DateTimeColumnTypeSimple,
DefaultColumnConfiguration,
GroupColumnDescriptor,
LookupRenderer,
Expand All @@ -32,6 +31,7 @@ import type {
TableHeading,
TableHeadings,
ValueListRenderer,
DateTimeColumnDescriptor,
} from "@finos/vuu-table-types";
import type { CSSProperties } from "react";
import { moveItem } from "./array-utils";
Expand Down Expand Up @@ -131,22 +131,11 @@ export const isNumericColumn = ({ serverDataType, type }: ColumnDescriptor) => {
return false;
};

type DateTimeColumnType =
| DateTimeColumnTypeSimple
| (Omit<ColumnTypeDescriptor, "name"> & { name: DateTimeColumnTypeSimple });

export type DateTimeColumnDescriptor = Omit<ColumnDescriptor, "type"> & {
type: DateTimeColumnType;
};

export const isDateColumn = ({ type }: ColumnDescriptor) =>
(isTypeDescriptor(type) ? type.name : type) === "date";
export const isTimeColumn = ({ type }: ColumnDescriptor) =>
(isTypeDescriptor(type) ? type.name : type) === "time";
export const isDateTimeColumn = (
column: ColumnDescriptor
): column is DateTimeColumnDescriptor =>
isDateColumn(column) || isTimeColumn(column);
(isTypeDescriptor(column.type) ? column.type.name : column.type) ===
"date/time";

export const isPinned = (column: ColumnDescriptor) =>
typeof column.pin === "string";
Expand Down
Loading

0 comments on commit 891fcc1

Please sign in to comment.