Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Validation for Date Picker #2165

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 80 additions & 14 deletions frontend/taipy-gui/src/components/Taipy/DateSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,21 @@ import { ErrorBoundary } from "react-error-boundary";
import { createSendUpdateAction } from "../../context/taipyReducers";
import { getSuffixedClassNames, TaipyActiveProps, TaipyChangeProps, DateProps, getProps, getCssSize } from "./utils";
import { dateToString, getDateTime, getTimeZonedDate } from "../../utils";
import { useClassNames, useDispatch, useDynamicProperty, useFormatConfig, useModule } from "../../utils/hooks";
import { useClassNames, useDispatch, useDynamicProperty, useFormatConfig, useModule, useDynamicJsonProperty } from "../../utils/hooks";
import Field from "./Field";
import ErrorFallback from "../../utils/ErrorBoundary";
import { getComponentClassName } from "./TaipyStyle";

interface DisableDateConfig {
disableWeekdays?: boolean;
disableWeekends?: boolean;
disablePastDays?: boolean;
disableFutureDays?: boolean;
dayOfWeek?: number;
interval?: number;
oddInterval?:boolean;
occurrence?: number;
}
interface DateSelectorProps extends TaipyActiveProps, TaipyChangeProps {
withTime?: boolean;
format?: string;
Expand All @@ -46,7 +56,9 @@ interface DateSelectorProps extends TaipyActiveProps, TaipyChangeProps {
editable?: boolean;
label?: string;
width?: string | number;
analogic? :boolean;
analogic?: boolean;
disableDateConfig?: string;
defaultDisableDateConfig?: string;
}

const boxSx = { display: "inline-block" };
Expand Down Expand Up @@ -74,6 +86,8 @@ const DateSelector = (props: DateSelectorProps) => {
const hover = useDynamicProperty(props.hoverText, props.defaultHoverText, undefined);
const min = useDynamicProperty(props.min, props.defaultMin, undefined);
const max = useDynamicProperty(props.max, props.defaultMax, undefined);
const emptyDateConfig = {} as Partial<DisableDateConfig>;
const disableDateConfig = useDynamicJsonProperty(props.disableDateConfig, props.defaultDisableDateConfig || "", emptyDateConfig);

const dateSx = useMemo(() => (props.width ? { maxWidth: getCssSize(props.width) } : undefined), [props.width]);

Expand Down Expand Up @@ -115,25 +129,76 @@ const DateSelector = (props: DateSelectorProps) => {
}
}, [props.date, tz, withTime, max, min]);


const getWeekNumberInMonth = (date: Date) => {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const firstDayOfMonth = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1));
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));

const weekNo: number = Math.ceil(((d.getTime() - firstDayOfMonth.getTime()) / 86400000 + 1) / 7);
return weekNo;
}
const isDisabledDate = (date: Date) => {
if (disableDateConfig) {
if (disableDateConfig.disableWeekdays && (date.getDay() == 0 || date.getDay() == 6)) {
return true;
}
if (disableDateConfig.disableWeekdays && (date.getDay() != 0 || date.getDay() != 6)) {
return true;
}
if (disableDateConfig.disablePastDays && (date < new Date())) {
return true
}
if (disableDateConfig.disableFutureDays && (date > new Date())) {
return true;
}

if (disableDateConfig.dayOfWeek) {
const isCorrectDay = date.getDay() === disableDateConfig.dayOfWeek;
const weekNumberInMonth = getWeekNumberInMonth(date);
const intervalCheck=disableDateConfig.oddInterval?1:0;
if (isCorrectDay && !disableDateConfig.interval && !disableDateConfig.occurrence) {
return true;
}
if (isCorrectDay && disableDateConfig.interval) {
if (weekNumberInMonth % disableDateConfig.interval === intervalCheck) {
return true;
}
}
if (isCorrectDay && disableDateConfig.occurrence) {
const dayOfMonth = new Date(date.getFullYear(), date.getMonth(), 1);
const dayDifference = (disableDateConfig.dayOfWeek - dayOfMonth.getDay() + 7) % 7;
const occurrenceDate = new Date(date.getFullYear(), date.getMonth(), 1 + dayDifference + (7 * (disableDateConfig.occurrence-1)));
if(occurrenceDate.getDate()==date.getDate()){
return true;
}
}
}
}
return false

};

return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Tooltip title={hover || ""}>
<Box id={id} className={`${className} ${getComponentClassName(props.children)}`} sx={boxSx}>
{editable ? (
withTime ? (
<DateTimePicker
{...(startProps as DateTimePickerProps<Date>)}
{...(endProps as DateTimePickerProps<Date>)}
value={value}
onChange={handleChange}
className={getSuffixedClassNames(className, "-picker")}
disabled={!active}
slotProps={textFieldProps}
label={props.label}
format={props.format}
sx={dateSx}
viewRenderers={ analogic ? analogicRenderers : undefined }
/>
{...(startProps as DateTimePickerProps<Date>)}
{...(endProps as DateTimePickerProps<Date>)}
value={value}
onChange={handleChange}
className={getSuffixedClassNames(className, "-picker")}
disabled={!active}
slotProps={textFieldProps}
label={props.label}
format={props.format}
sx={dateSx}
viewRenderers={analogic ? analogicRenderers : undefined}
shouldDisableDate={isDisabledDate}
/>
) : (
<DatePicker
{...(startProps as DatePickerProps<Date>)}
Expand All @@ -146,6 +211,7 @@ const DateSelector = (props: DateSelectorProps) => {
label={props.label}
format={props.format}
sx={dateSx}
shouldDisableDate={isDisabledDate}
/>
)
) : (
Expand Down
1 change: 1 addition & 0 deletions taipy/gui/_renderers/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class _Factory:
("on_change", PropertyType.function),
("format",),
("width", PropertyType.string_or_number),
("disable_date_config",PropertyType.dynamic_dict)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SHould this be dynamic ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be dynamic, as some keys in the dictionary are optional. Please correct me if I'm wrong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be dynamic, as some keys in the dictionary are optional. Please correct me if I'm wrong.

Dynamic means that the bound variable can be updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the clarification, @FredLL-Avaiga. I will change it to dict.

I have fixed the checks that failed due to spelling. The remaining tests are failing because no test cases were written, correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are some tests failing due to an exterior factor (they're failing in the rest subtree)

]
)
._set_propagate(),
Expand Down
Loading