Skip to content

Commit

Permalink
Merge pull request #18 from Jaspersoft/date-time-ic
Browse files Browse the repository at this point in the history
DateTime Input Control
  • Loading branch information
grantbacon-jaspersoft authored Jul 9, 2024
2 parents 879e8ba + 1b13cb9 commit d3d88f0
Show file tree
Hide file tree
Showing 10 changed files with 449 additions and 114 deletions.
4 changes: 4 additions & 0 deletions packages/jv-input-controls/src/InputControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from "react";
import { BoolICType } from "./controls/BooleanInputControl";
import { createRoot } from "react-dom/client";
import { DateICType } from "./controls/DatePickerInputControl";
import { DateTimeICType } from "./controls/DateTimePickerInputControl";
import { TextFieldICType } from "./controls/SingleValueTextInputControl";
import BasePanel from "./panels/BasePanel";
import { InputControlCollection } from "./controls/BaseInputControl";
Expand All @@ -26,6 +27,9 @@ export interface InputControlUserConfig {
singleValueDate?: {
type: DateICType;
};
singleValueDatetime?: {
type: DateTimeICType;
};
}

export interface InputControlPanelConfig {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { DateTimePicker as JVDateTimePicker } from "@jaspersoft/jv-ui-components/material-ui/DateTime/DateTimePicker";
import {
BaseInputControlProps,
ICDateValidationRule,
} from "./BaseInputControl";
import { useControlClasses } from "./hooks/useControlClasses";
import { useLiveDateFormattedState } from "./hooks/useLiveDateFormattedState";

export type DateTimeICType = "datetime";

export interface DateTimeICProps extends BaseInputControlProps {
value?: string;
className?: string;
views?: Array<"year" | "month" | "day" | "hours" | "minutes" | "seconds">;
disabled?: boolean;
}

const removeSingleQuotes = (str: string) => str.replace(/'/g, "");
const formatToDayJS = (str: string) => {
const separator = str.includes("T") ? "T" : " ";
const [date, time] = str.split(separator);
return `${date.toUpperCase()}${separator}${time}`;
};

export const DateTimePickerInputControl = (props: DateTimeICProps) => {
let dateFormat,
views: string[] = [];
if (props.validationRules !== undefined) {
const [rule] = props.validationRules as ICDateValidationRule[];
dateFormat = removeSingleQuotes(rule.dateTimeFormatValidationRule.format);
dateFormat = formatToDayJS(dateFormat);
}
views = props.views
? props.views
: ["year", "month", "day", "hours", "minutes", "seconds"];
const liveState = useLiveDateFormattedState({
initialValue: props.state?.value || props.value || "",
format: dateFormat,
});
const controlClasses = useControlClasses([], props);
return (
<JVDateTimePicker
{...props}
onChange={liveState.onChange}
value={liveState.value}
views={views}
className={`${controlClasses.join(" ")} ${props.className || ""}`}
timeSteps={{ hours: 1, minutes: 1, seconds: 1 }}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function useLiveDateFormattedState({
const [value, setValue] = useState(initialValue);

function handleChange(e: any) {
setValue(e.format(format));
setValue(e && e.format ? e.format(format) : e);
}

return {
Expand Down
18 changes: 17 additions & 1 deletion packages/jv-input-controls/src/panels/BasePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { DatePickerProvider as JVDatePickerProvider } from "@jaspersoft/jv-ui-components/material-ui/Date/DatePickerProvider";
import * as React from "react";
import BooleanInputControl from "../controls/BooleanInputControl";
import { DatePickerInputControl } from "../controls/DatePickerInputControl";
import { DatePickerProvider as JVDatePickerProvider } from "@jaspersoft/jv-ui-components/material-ui/Date/DatePickerProvider";
import { DateTimePickerInputControl } from "../controls/DateTimePickerInputControl";
import { SingleValueTextInputControl } from "../controls/SingleValueTextInputControl";
import { InputControlUserConfig } from "../InputControls";

Expand Down Expand Up @@ -60,6 +61,21 @@ export default function BasePanel(props: BasePanelProps): React.JSX.Element {
/>
);
}
if (control.type === "singleValueDatetime") {
return (
<DateTimePickerInputControl
key={control.id}
id={control.id}
label={control.label}
value={control.state.value}
type={control.type}
readOnly={control.readOnly}
visible={control.visible}
mandatory={control.mandatory}
validationRules={control.validationRules}
/>
);
}
};

const buildControls = (controlMap: any) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { DatePickerProvider as JVDatePickerProvider } from "@jaspersoft/jv-ui-components/material-ui/Date/DatePickerProvider";
import { fireEvent, render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import * as React from "react";
import { DateTimePickerInputControl } from "../src/controls/DateTimePickerInputControl";

const requiredProps = {
id: "column_timestamp_1",
label: "column_timestamp",
mandatory: false,
readOnly: false,
visible: true,
type: "singleValueDatetime",
};

const getDateTimePickerIC = (options?: any): React.JSX.Element => {
const mergedProps = { ...requiredProps, ...options };
return (
<JVDatePickerProvider>
<DateTimePickerInputControl {...mergedProps} />
</JVDatePickerProvider>
);
};

describe("DateTimePickerInputControl tests", () => {
test("DateTimePickerInputControl is rendered correctly", () => {
render(getDateTimePickerIC({ value: "2014-09-12T15:46:18" }));
const datePickerElement = screen.getByRole("textbox");
expect(datePickerElement).toBeInTheDocument();
});

test("displays the label when provided", () => {
const testLabel = "Test Label";
render(getDateTimePickerIC({ label: testLabel }));
const labelElement = screen.queryByLabelText(testLabel);
expect(labelElement).toBeInTheDocument();
});

test("value is converted to AM/PM format", () => {
render(getDateTimePickerIC({ value: "2014-09-14T15:46:18" }));
const inputElement = screen.getByRole("textbox") as HTMLInputElement;
expect(inputElement.value).toBe("09/14/2014 03:46:18 PM");
});

test("check the component is read-only", () => {
// Render the component
const { rerender } = render(getDateTimePickerIC({ readOnly: true }));
let inputElement = screen.getByRole("textbox") as HTMLInputElement;

// Assert that the element is found and has the expected attribute
expect(inputElement).toBeInTheDocument();
expect(inputElement).toHaveAttribute("readonly");

rerender(getDateTimePickerIC({}));
inputElement = screen.getByRole("textbox") as HTMLInputElement;
expect(inputElement).not.toHaveAttribute("readonly");
});

test("check the component is visible or not", () => {
const HIDDEN_CLASS_NAME = "jv-uVisibility-hide";
// Render the component
const { container } = render(getDateTimePickerIC({ visible: false }));
// Use querySelector to get the first div with the class "jv-mInputLarge"
const divElement = container.querySelector(`div.${HIDDEN_CLASS_NAME}`);

// Assert that the element is found and has the expected class
expect(divElement).toBeInTheDocument();
expect(divElement).toHaveClass(HIDDEN_CLASS_NAME);
});

test("check the component is disabled", () => {
// Render the component
const { rerender } = render(getDateTimePickerIC({ disabled: true }));
let inputElement = screen.getByRole("textbox") as HTMLInputElement;

// Assert that the element is found and has the expected attribute
expect(inputElement).toBeInTheDocument();
expect(inputElement).toHaveAttribute("disabled");

rerender(getDateTimePickerIC({}));
inputElement = screen.getByRole("textbox") as HTMLInputElement;
expect(inputElement).not.toHaveAttribute("disabled");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright © 2005-2024. Cloud Software Group, Inc. All rights reserved. Confidential & Proprietary.
* Licensed pursuant to commercial Cloud Software Group, Inc End User License Agreement.
*/

import { useLiveDateFormattedState } from "../../src/controls/hooks/useLiveDateFormattedState";
import { renderHook } from "@testing-library/react";

describe("useLiveState hook tests", () => {
const initialValue = "2024-07-08";

it("should store initial value", () => {
const { result } = renderHook(() =>
useLiveDateFormattedState({ initialValue }),
);
expect(result.current.value).toBe(initialValue);
});
it("should return 2 fields", () => {
const { result } = renderHook(() =>
useLiveDateFormattedState({ initialValue }),
);
expect(Object.keys(result.current).length).toBe(2);
});
it("should return expected fields: value, onChange", () => {
const { result } = renderHook(() =>
useLiveDateFormattedState({ initialValue }),
);
expect(result.current.value).toBeDefined();
expect(result.current.onChange).toBeDefined();
expect(result.current.onChange instanceof Function).toBeTruthy();
});
it("should return same date even though a datetime format is provided", () => {
const dateTimeFormat = "YYYY-MM-DDTHH:mm:ss";
const { result } = renderHook(() =>
useLiveDateFormattedState({ format: dateTimeFormat, initialValue }),
);
expect(result.current.value).toBe(initialValue);
});
it("should return a datetime value", () => {
const dateTimeFormat = "YYYY-MM-DDTHH:mm:ss",
dateTimeValue = "2014-09-12T15:46:18";
const { result } = renderHook(() =>
useLiveDateFormattedState({
format: dateTimeFormat,
initialValue: dateTimeValue,
}),
);
expect(result.current.value).toBe(dateTimeValue);
});
});
12 changes: 12 additions & 0 deletions packages/jv-ui-components/material-ui/Date/Date.Utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright © 2005-2024. Cloud Software Group, Inc. All rights reserved. Confidential & Proprietary.
* Licensed pursuant to commercial Cloud Software Group, Inc End User License Agreement.
*/

import dayjs from "dayjs";

const castValueIfNeeded = (theValue: dayjs.Dayjs): dayjs.Dayjs => {
return theValue instanceof dayjs ? theValue : dayjs(theValue);
};

export default castValueIfNeeded;
6 changes: 1 addition & 5 deletions packages/jv-ui-components/material-ui/Date/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { DatePicker as MuiDatePicker } from "@mui/x-date-pickers";
import dayjs, { Dayjs } from "dayjs";
import { forwardRef } from "react";

const castValueIfNeeded = (theValue: Dayjs | string): Dayjs => {
return theValue instanceof dayjs ? theValue : dayjs(theValue);
};
import castValueIfNeeded from "./Date.Utils";

export const DatePicker = forwardRef((props: any, ref) => {
const { defaultValue, value, ...remainingProps } = props;
Expand Down
13 changes: 13 additions & 0 deletions packages/jv-ui-components/material-ui/DateTime/DateTimePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { DateTimePicker as MuiDateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
import { forwardRef } from "react";
import castValueIfNeeded from "../Date/Date.Utils";

export const DateTimePicker = forwardRef((props: any, ref) => {
const { value, ...remainingProps } = props;

let otherProps = {};
if (value) {
otherProps = { value: castValueIfNeeded(value) };
}
return <MuiDateTimePicker {...{ ...remainingProps, ...otherProps }} />;
});
Loading

0 comments on commit d3d88f0

Please sign in to comment.