@inera/ids-react 9.2.2 → 9.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/components/accordion/accordion.js +1 -1
  2. package/components/alert/alert-base.js +2 -2
  3. package/components/breadcrumbs/breadcrumbs.js +6 -1
  4. package/components/button/button.d.ts +1 -0
  5. package/components/button/button.js +2 -2
  6. package/components/button/control-button.d.ts +6 -0
  7. package/components/button/control-button.js +9 -0
  8. package/components/carousel/carousel.js +1 -1
  9. package/components/dialog/dialog-base.js +1 -1
  10. package/components/form/checkbox/checkbox-base.js +2 -3
  11. package/components/form/checkbox/checkbox-group-base.d.ts +2 -1
  12. package/components/form/checkbox/checkbox-group-base.js +3 -3
  13. package/components/form/checkbox/checkbox-group.d.ts +2 -1
  14. package/components/form/checkbox/checkbox-group.js +2 -2
  15. package/components/form/checkbox/checkbox.js +3 -4
  16. package/components/form/datepicker/datepicker.d.ts +3 -1
  17. package/components/form/datepicker/datepicker.js +142 -110
  18. package/components/form/form-hooks/useInputValidity.d.ts +1 -1
  19. package/components/form/form-hooks/useInputValidity.js +28 -12
  20. package/components/form/form-props/form-props.d.ts +1 -0
  21. package/components/form/input/input-base.d.ts +4 -2
  22. package/components/form/input/input-base.js +14 -9
  23. package/components/form/input/input.d.ts +3 -0
  24. package/components/form/input/input.js +1 -1
  25. package/components/form/radio/radio-base.js +1 -2
  26. package/components/form/radio/radio-group-base.d.ts +2 -1
  27. package/components/form/radio/radio-group-base.js +3 -3
  28. package/components/form/radio/radio-group.d.ts +2 -1
  29. package/components/form/radio/radio-group.js +2 -2
  30. package/components/form/range/range-base.d.ts +1 -1
  31. package/components/form/range/range-base.js +2 -2
  32. package/components/form/select/select-base.d.ts +3 -3
  33. package/components/form/select/select-base.js +3 -5
  34. package/components/form/select/select.d.ts +2 -2
  35. package/components/form/select/select.js +1 -1
  36. package/components/form/select-multiple/select-multiple-base.d.ts +1 -1
  37. package/components/form/select-multiple/select-multiple-base.js +2 -2
  38. package/components/form/textarea/textarea-base.d.ts +1 -1
  39. package/components/form/textarea/textarea-base.js +3 -5
  40. package/components/form/textarea/textarea.js +1 -1
  41. package/components/form/time/time-base.d.ts +1 -1
  42. package/components/form/time/time-base.js +2 -4
  43. package/components/form/time/time.js +4 -5
  44. package/components/header-1177/header-1177-region-picker-base.d.ts +3 -1
  45. package/components/header-1177/header-1177-region-picker-base.js +8 -3
  46. package/components/header-1177/header-1177-region-picker-mobile-base.d.ts +3 -1
  47. package/components/header-1177/header-1177-region-picker-mobile-base.js +8 -3
  48. package/components/header-1177/header-1177-region-picker-mobile.d.ts +2 -0
  49. package/components/header-1177/header-1177-region-picker.d.ts +2 -0
  50. package/components/header-1177-pro/header-1177-pro-region-picker-base.d.ts +3 -1
  51. package/components/header-1177-pro/header-1177-pro-region-picker-base.js +8 -3
  52. package/components/header-1177-pro/header-1177-pro-region-picker-mobile-base.d.ts +3 -1
  53. package/components/header-1177-pro/header-1177-pro-region-picker-mobile-base.js +8 -3
  54. package/components/header-1177-pro/header-1177-pro-region-picker-mobile.d.ts +3 -1
  55. package/components/header-1177-pro/header-1177-pro-region-picker-mobile.js +1 -1
  56. package/components/header-1177-pro/header-1177-pro-region-picker.d.ts +3 -1
  57. package/components/header-1177-pro/header-1177-pro-region-picker.js +1 -1
  58. package/components/popover/popover-content.js +1 -1
  59. package/components/popover/popover.d.ts +2 -1
  60. package/components/popover/popover.js +28 -14
  61. package/components/puff-list/puff-list-item-header.d.ts +1 -1
  62. package/components/puff-list/puff-list-item.d.ts +2 -1
  63. package/components/puff-list/puff-list-item.js +2 -2
  64. package/components/side-panel/side-panel-base.js +3 -1
  65. package/components/stepper/step-base.d.ts +1 -1
  66. package/components/stepper/step-base.js +1 -1
  67. package/components/stepper/step.d.ts +1 -1
  68. package/components/stepper/step.js +2 -11
  69. package/components/tag/tag.js +8 -2
  70. package/components/tooltip/tooltip-base.d.ts +1 -3
  71. package/components/tooltip/tooltip-base.js +14 -10
  72. package/components/tooltip/tooltip.js +1 -41
  73. package/index.d.ts +1 -0
  74. package/index.js +1 -0
  75. package/package.json +4 -3
@@ -9,7 +9,7 @@ function IDSAccordion({ headline, subtitle, accordions, level = 1, headlineSize
9
9
  const [isLean, setIsLean] = useState(lean);
10
10
  const accordionRef = useRef(null);
11
11
  const isExpanded = isControlled ? expanded : internalExpanded;
12
- const hasChildren = React.Children.toArray(children).some(child => {
12
+ const hasChildren = React.Children.toArray(children).some((child) => {
13
13
  return React.isValidElement(child) && child.type === IDSAccordion;
14
14
  });
15
15
  useEffect(() => {
@@ -21,9 +21,9 @@ function IDSAlertBase({ collapsible = false, ribbon = false, collapsed = false,
21
21
  "ids-alert--ribbon": ribbon,
22
22
  "ids-alert--compact": compact,
23
23
  "ids-alert--dismissible": dismissible
24
- }, className), children: compact || ribbon ? (jsxs(Fragment, { children: [jsxs("div", { className: "ids-alert__content", children: [jsx("span", { className: "ids-alert__content-icon", "aria-hidden": "true" }), jsx("span", { className: "ids-alert__content-text", children: children })] }), dismissible && (jsx("button", { type: "button", className: "ids-alert__close", ...closeHandler, "aria-label": srCloseText }))] })) : (jsxs(Fragment, { children: [jsxs("div", { className: "ids-alert__header", children: [collapsible && !compact && !ribbon && (jsx("button", { type: "button", className: clsx("ids-alert__expand-button", {
24
+ }, className), children: compact || ribbon ? (jsxs(Fragment, { children: [jsxs("div", { className: "ids-alert__content", children: [jsx("span", { className: "ids-alert__content-icon", "aria-hidden": "true" }), jsx("span", { className: "ids-alert__content-text", children: children })] }), dismissible && (jsx("div", { className: "ids-alert__button", children: jsx("button", { className: "ids-alert__close", "aria-label": srCloseText, ...closeHandler }) }))] })) : (jsxs(Fragment, { children: [jsxs("div", { className: "ids-alert__header", children: [collapsible && !compact && !ribbon && (jsx("button", { type: "button", className: clsx("ids-alert__expand-button", {
25
25
  "ids-alert__expand-button--expanded": !collapsed
26
- }), ...toggleHandler, "aria-controls": contentId, "aria-expanded": !collapsed, children: jsxs("div", { className: "ids-alert__icon_and_text", children: [jsx("span", { className: "ids-alert__state-icon", "aria-hidden": "true" }), jsx("div", { className: "ids-alert__headline", children: collapsableHeadline }), jsx("span", { className: "ids-alert__sr-only", children: collapsed ? srExpandText : srCollapseText })] }) })), !collapsible && (jsxs("div", { className: "ids-alert__icon_and_text", children: [jsx("span", { className: "ids-alert__state-icon", "aria-hidden": "true" }), jsx("div", { className: "ids-alert__headline", children: headline })] })), dismissible && !collapsible && (jsx("button", { type: "button", className: "ids-alert__close", ...closeHandler, "aria-label": srCloseText }))] }), jsx("div", { id: contentId, children: !collapsed && jsx("div", { className: "ids-alert__content", children: children }) })] })) }));
26
+ }), ...toggleHandler, "aria-controls": contentId, "aria-expanded": !collapsed, children: jsxs("div", { className: "ids-alert__icon_and_text", children: [jsx("span", { className: "ids-alert__state-icon", "aria-hidden": "true" }), jsx("div", { className: "ids-alert__headline", children: collapsableHeadline }), jsx("span", { className: "ids-alert__sr-only", children: collapsed ? srExpandText : srCollapseText })] }) })), !collapsible && (jsxs("div", { className: "ids-alert__icon_and_text", children: [jsx("span", { className: "ids-alert__state-icon", "aria-hidden": "true" }), jsx("div", { className: "ids-alert__headline", children: headline })] })), dismissible && !collapsible && (jsx("div", { className: "ids-alert__button", children: jsx("button", { className: "ids-alert__close", "aria-label": srCloseText, ...closeHandler }) }))] }), jsx("div", { id: contentId, children: !collapsed && jsx("div", { className: "ids-alert__content", children: children }) })] })) }));
27
27
  }
28
28
  IDSAlertBase.displayName = "IDSAlertBase";
29
29
 
@@ -37,7 +37,12 @@ function IDSBreadcrumbs({ lead = "Du är här", mobileLink, children, className,
37
37
  return links.flatMap((child, index) => {
38
38
  const isLast = index === links.length - 1;
39
39
  if (isLast) {
40
- return (jsx("li", { className: "ids-breadcrumbs__crumb ids-breadcrumbs__crumb--current", "data-crumb": true, children: child }, index));
40
+ const currentChild = React.isValidElement(child)
41
+ ? React.cloneElement(child, {
42
+ "aria-current": "page"
43
+ })
44
+ : child;
45
+ return (jsx("li", { className: "ids-breadcrumbs__crumb ids-breadcrumbs__crumb--current", "data-crumb": true, children: currentChild }, index));
41
46
  }
42
47
  else {
43
48
  return (jsxs("li", { className: "ids-breadcrumbs__crumb", "data-crumb": true, children: [child, jsx("span", { className: "ids-breadcrumbs__crumb__separator", "aria-hidden": "true", children: "/" })] }, index));
@@ -7,6 +7,7 @@ export interface IDSButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>
7
7
  fab?: boolean;
8
8
  focusAnchor?: boolean;
9
9
  icon?: string;
10
+ endIcon?: string;
10
11
  iconButton?: boolean;
11
12
  loading?: boolean;
12
13
  mBlock?: boolean;
@@ -3,7 +3,7 @@ import clsx from 'clsx';
3
3
  import { forwardRef } from 'react';
4
4
  import { IDSSpinner } from '../form/spinner/spinner.js';
5
5
 
6
- const IDSButton = forwardRef(({ active = false, block = false, disabled = false, fab = false, iconButton = false, icon = "", mBlock = false, sBlock = false, loading = false, tertiary = false, secondary = false, size = "m", submit = false, toggle = false, focusAnchor = false, children, className, ...props }, ref) => {
6
+ const IDSButton = forwardRef(({ active = false, block = false, disabled = false, fab = false, iconButton = false, icon = "", endIcon = "", mBlock = false, sBlock = false, loading = false, tertiary = false, secondary = false, size = "m", submit = false, toggle = false, focusAnchor = false, children, className, ...props }, ref) => {
7
7
  let classNames;
8
8
  if (fab) {
9
9
  classNames = clsx("ids-button", {
@@ -26,7 +26,7 @@ const IDSButton = forwardRef(({ active = false, block = false, disabled = false,
26
26
  "ids-focus-anchor": focusAnchor
27
27
  }, className);
28
28
  }
29
- return (jsxs("button", { ...props, ref: ref, className: classNames, "aria-disabled": disabled || loading, ...(toggle ? { "aria-pressed": active } : {}), tabIndex: !disabled && !loading ? 0 : -1, disabled: disabled, children: [icon && jsx("span", { className: `ids-icon-${icon}` }), loading && jsx(IDSSpinner, { variant: !secondary && !tertiary ? "3" : "2" }), !loading && children] }));
29
+ return (jsxs("button", { ...props, ref: ref, className: classNames, "aria-disabled": disabled || loading, ...(toggle ? { "aria-pressed": active } : {}), tabIndex: !disabled && !loading ? 0 : -1, disabled: disabled, children: [icon && jsx("span", { className: `ids-icon-${icon}`, "aria-hidden": "true" }), loading && jsx(IDSSpinner, { variant: !secondary && !tertiary ? "3" : "2" }), !loading && children, endIcon && jsx("span", { className: `ids-icon-${endIcon}` })] }));
30
30
  });
31
31
  IDSButton.displayName = "IDSButton";
32
32
 
@@ -0,0 +1,6 @@
1
+ import { ButtonHTMLAttributes } from "react";
2
+ export interface IDSControlButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
3
+ icon: string;
4
+ disabled?: boolean;
5
+ }
6
+ export declare const IDSControlButton: import("react").ForwardRefExoticComponent<IDSControlButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,9 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { forwardRef } from 'react';
3
+
4
+ const IDSControlButton = forwardRef(({ disabled = false, icon, className, ...props }, ref) => {
5
+ return (jsx("button", { className: "ids-control-button", ...props, ref: ref, "aria-disabled": disabled, disabled: disabled, children: jsx("span", { className: `ids-icon-${icon}`, "aria-hidden": "true" }) }));
6
+ });
7
+ IDSControlButton.displayName = "IDSControlButton";
8
+
9
+ export { IDSControlButton };
@@ -23,7 +23,7 @@ function IDSCarousel({ srNextLabel = "Nästa", srPrevLabel = "Föregående", cla
23
23
  // Validate slide count
24
24
  useEffect(() => {
25
25
  if (slideCount < 3) {
26
- console.warn("Carousel requires at least 3 carousel-items");
26
+ console.warn("Carousel requires at least 4 carousel-items");
27
27
  }
28
28
  }, [slideCount]);
29
29
  // Initial setup
@@ -20,7 +20,7 @@ const IDSDialogBase = forwardRef(({ show = false, width, maxWidth, height, headl
20
20
  "ids-dialog--hidden": !show,
21
21
  "ids-focus-trap": !noFocusTrap,
22
22
  "ids-dialog--dismissible": !!dismissible
23
- }, className), ref: ref, role: "dialog", "aria-modal": "true", "aria-labelledby": headline ? headlineId : undefined, style: { width, maxWidth, height, maxHeight: height || undefined }, children: [dismissible && (jsx("div", { className: "ids-dialog__dismissible", children: jsx("button", { "aria-label": srClose, ...closeHandler, className: "ids-dialog__close-button" }) })), jsxs("div", { ref: scrollAreaRef, className: clsx("ids-dialog__body", {
23
+ }, className), ref: ref, role: "dialog", "aria-modal": "true", "aria-labelledby": headline ? headlineId : undefined, style: { width, maxWidth, height, maxHeight: height || undefined }, children: [dismissible && (jsx("div", { className: "ids-dialog__dismissible", children: jsx("button", { className: "ids-dialog__close-button", "aria-label": srClose, ...closeHandler }) })), jsxs("div", { ref: scrollAreaRef, className: clsx("ids-dialog__body", {
24
24
  "ids-dialog__body--scroll-area-focus": !noScrollAreaFocus
25
25
  }), tabIndex: noScrollAreaFocus ? -1 : 0, children: [jsx("div", { className: "ids-dialog__body-headline", id: headlineId, children: headline }), jsx("div", { className: "ids-dialog__body-content", children: children }), actions && jsx("div", { className: "ids-dialog__footer", children: actions })] })] })] }));
26
26
  });
@@ -16,13 +16,12 @@ function IDSCheckboxBase({ invalid, disabled, required, indeterminate, light, bl
16
16
  return (jsxs("div", { className: clsx("ids-checkbox-component", className), style: { display: "inline-flex", flexDirection: "column", gap: showErrorMsg ? "0.5rem" : "" }, "data-testid": dataTestId, children: [jsxs("div", { className: clsx("ids-checkbox", {
17
17
  "ids-checkbox--light": light,
18
18
  "ids-checkbox--block": block
19
- }), "data-testid": dataTestId, children: [jsx("input", { id: inputId, ref: inputRef, type: "checkbox", className: clsx("ids-checkbox__input", {
19
+ }), children: [jsx("input", { id: inputId, ref: inputRef, type: "checkbox", className: clsx("ids-checkbox__input", {
20
20
  "ids-focus-anchor": focusAnchor
21
21
  }), "aria-invalid": invalid, disabled: disabled, required: required, ...ariaErrorHandler, ...ariaCheckedHandler, ...props }), !!children && (jsxs("div", { className: clsx("ids-label-wrapper", {
22
22
  "ids-label-wrapper--block": block
23
23
  }), children: [jsx("label", { htmlFor: inputId, className: clsx("ids-checkbox__label ids-label", {
24
- "ids-label--clickable": !disabled,
25
- "ids-label--disabled": disabled
24
+ "ids-label--clickable": !disabled
26
25
  }), children: children }), tooltip && jsx("span", { className: "ids-label__tooltip", children: tooltip })] }))] }), showErrorMsg && (jsx(IDSErrorMessage, { id: baseErrorMsgId, show: true, children: errorMsg }))] }));
27
26
  }
28
27
  IDSCheckboxBase.displayName = "IDSCheckboxBase";
@@ -9,8 +9,9 @@ export interface IDSCheckboxGroupBaseProps extends InputHTMLAttributes<HTMLField
9
9
  invalid?: boolean;
10
10
  errorMsgId?: string;
11
11
  groupRef?: React.Ref<HTMLFieldSetElement>;
12
+ subtitle?: string | ReactNode;
12
13
  }
13
- export declare function IDSCheckboxGroupBase({ legend, errorMsg, compact, hideLegend, tooltip, children, className, invalid, errorMsgId, groupRef, ...props }: IDSCheckboxGroupBaseProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function IDSCheckboxGroupBase({ legend, errorMsg, subtitle, compact, hideLegend, tooltip, children, className, invalid, errorMsgId, groupRef, ...props }: IDSCheckboxGroupBaseProps): import("react/jsx-runtime").JSX.Element;
14
15
  export declare namespace IDSCheckboxGroupBase {
15
16
  var displayName: string;
16
17
  }
@@ -3,13 +3,13 @@ import { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
  import { IDSErrorMessage } from '../error-message/error-message.js';
5
5
 
6
- function IDSCheckboxGroupBase({ legend, errorMsg = "", compact = false, hideLegend, tooltip, children, className, invalid = false, errorMsgId, groupRef, ...props }) {
6
+ function IDSCheckboxGroupBase({ legend, errorMsg = "", subtitle, compact = false, hideLegend, tooltip, children, className, invalid = false, errorMsgId, groupRef, ...props }) {
7
7
  const baseErrorMsgId = errorMsgId ?? `checkbox-group-base-error-${useId()}`;
8
8
  return (jsxs("fieldset", { ref: groupRef, "aria-describedby": invalid && errorMsg ? baseErrorMsgId : undefined, className: clsx("ids-form-group__fieldset", {
9
9
  "ids-form-group__fieldset--compact": compact
10
- }, className), ...props, children: [legend && (jsx("div", { className: clsx("ids-label-wrapper", {
10
+ }, className), ...props, children: [legend && (jsxs("div", { className: clsx("ids-label-wrapper", {
11
11
  "ids-label-wrapper--sr-only": hideLegend
12
- }, className), children: jsxs("legend", { children: [legend, tooltip && jsx("span", { className: "ids-legend__tooltip", children: tooltip })] }) })), children, invalid && !!errorMsg && (jsx(IDSErrorMessage, { id: baseErrorMsgId, show: true, compact: true, style: { marginTop: compact ? "0.75rem" : "auto" }, children: errorMsg }))] }));
12
+ }, className), children: [jsxs("legend", { children: [legend, tooltip && jsx("span", { className: "ids-legend__tooltip", children: tooltip })] }), subtitle && jsx("div", { className: "ids-subtitle ids-subtitle--group", children: subtitle })] })), children, invalid && !!errorMsg && (jsx(IDSErrorMessage, { id: baseErrorMsgId, show: true, compact: true, style: { marginTop: compact ? "0.75rem" : "auto" }, children: errorMsg }))] }));
13
13
  }
14
14
  IDSCheckboxGroupBase.displayName = "IDSCheckboxGroupBase";
15
15
 
@@ -8,10 +8,11 @@ export interface IDSCheckboxGroupProps extends InputHTMLAttributes<HTMLFieldSetE
8
8
  tooltip?: ReactNode;
9
9
  block?: boolean;
10
10
  noValidation?: boolean;
11
+ subtitle?: string | ReactNode;
11
12
  children?: ReactNode;
12
13
  onValidityChange?: (isValid: boolean) => void;
13
14
  }
14
- export declare function IDSCheckboxGroup({ errorMsg, block, invalid, noValidation, onValidityChange, children, ...props }: IDSCheckboxGroupProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function IDSCheckboxGroup({ errorMsg, block, invalid, subtitle, noValidation, onValidityChange, children, ...props }: IDSCheckboxGroupProps): import("react/jsx-runtime").JSX.Element;
15
16
  export declare namespace IDSCheckboxGroup {
16
17
  var displayName: string;
17
18
  }
@@ -5,7 +5,7 @@ import { IDSCheckboxGroupBase } from './checkbox-group-base.js';
5
5
  import { IDSCheckbox } from './checkbox.js';
6
6
  import { useGroupValidity } from '../form-hooks/useGroupValidity.js';
7
7
 
8
- function IDSCheckboxGroup({ errorMsg = "", block, invalid, noValidation = false, onValidityChange, children, ...props }) {
8
+ function IDSCheckboxGroup({ errorMsg = "", block, invalid, subtitle, noValidation = false, onValidityChange, children, ...props }) {
9
9
  const groupRef = useRef(null);
10
10
  const errorMsgId = useId();
11
11
  const { isValid, hasInteracted } = useGroupValidity(groupRef, "checkbox", () => {
@@ -34,7 +34,7 @@ function IDSCheckboxGroup({ errorMsg = "", block, invalid, noValidation = false,
34
34
  }
35
35
  return child;
36
36
  });
37
- return (jsx(IDSCheckboxGroupBase, { ...props, groupRef: groupRef, invalid: groupInvalid || invalid, errorMsgId: errorMsgId, errorMsg: !noValidation && errorMsg, children: clonedChildren }));
37
+ return (jsx(IDSCheckboxGroupBase, { ...props, groupRef: groupRef, invalid: groupInvalid || invalid, errorMsgId: errorMsgId, errorMsg: !noValidation && errorMsg, subtitle: subtitle, children: clonedChildren }));
38
38
  }
39
39
  IDSCheckboxGroup.displayName = "IDSCheckboxGroup";
40
40
 
@@ -4,10 +4,9 @@ import { forwardRef, useRef, useEffect } from 'react';
4
4
  import { IDSCheckboxBase } from './checkbox-base.js';
5
5
  import { useInputValidity } from '../form-hooks/useInputValidity.js';
6
6
 
7
- const IDSCheckbox = forwardRef(({ invalid = false, noValidation = false, indeterminate = false, children, ...props }, ref) => {
8
- const internalRef = useRef(null);
9
- const checkboxRef = ref ?? internalRef;
10
- const hasValidValue = useInputValidity(checkboxRef);
7
+ const IDSCheckbox = forwardRef(({ invalid = false, noValidation = false, indeterminate = false, validationOnBlur = false, children, ...props }, ref) => {
8
+ const checkboxRef = useRef(null);
9
+ const hasValidValue = useInputValidity(checkboxRef, validationOnBlur, noValidation);
11
10
  const computedInvalid = (invalid || !hasValidValue) && !noValidation;
12
11
  // Merge forwarded + local ref
13
12
  const mergedRef = (node) => {
@@ -21,13 +21,15 @@ export interface IDSDatePickerProps extends Omit<React.InputHTMLAttributes<HTMLI
21
21
  invalidDateErrorMsg?: string;
22
22
  disableNavigation?: boolean;
23
23
  noValidation?: boolean;
24
+ readOnly?: boolean;
25
+ dataTestId?: string;
24
26
  modifiers?: Record<string, Matcher | Matcher[]>;
25
27
  onChange?: (event: IDSDatePickerChangeEvent) => void;
26
28
  onOpen?: () => void;
27
29
  onClose?: () => void;
28
30
  onDayClick?: (date: Date, modifiers: Modifiers, e: React.MouseEvent) => void;
29
31
  }
30
- export declare function IDSDatePicker({ label, id, value, light, placeholder, errorMsg, missingDateErrorMsg, invalidDateErrorMsg, calendarHeader, srOpenText, srCloseText, validationOnBlur, defaultMonth, startMonth, endMonth, noValidation, disabled, required, invalid, tooltip, disableNavigation, modifiers, focusedDay, onChange, onFocus, onBlur, onOpen, onClose, onDayClick, className, ...props }: IDSDatePickerProps): import("react/jsx-runtime").JSX.Element;
32
+ export declare function IDSDatePicker({ label, id, value, light, placeholder, subtitle, dataTestId, errorMsg, missingDateErrorMsg, invalidDateErrorMsg, calendarHeader, srOpenText, srCloseText, validationOnBlur, defaultMonth, startMonth, endMonth, noValidation, disabled, required, invalid, readOnly, tooltip, disableNavigation, modifiers, focusedDay, onChange, onFocus, onBlur, onOpen, onClose, onDayClick, className, ...props }: IDSDatePickerProps): import("react/jsx-runtime").JSX.Element;
31
33
  export declare namespace IDSDatePicker {
32
34
  var displayName: string;
33
35
  }
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { useId, useRef, useState, useEffect } from 'react';
3
- import { isValid, getWeek, format, subMonths, addMonths } from 'date-fns';
3
+ import { getWeek, isValid, format, subMonths, addMonths } from 'date-fns';
4
4
  import { sv } from 'react-day-picker/locale';
5
5
  import clsx from 'clsx';
6
6
  import { IDSErrorMessage } from '../error-message/error-message.js';
@@ -10,6 +10,8 @@ import { useAriaDescribedBy } from '../form-hooks/useAriaDescribedBy.js';
10
10
  import { useClickOutside } from '../../utils/hooks/useClickOutside.js';
11
11
 
12
12
  const locale = { locale: sv };
13
+ const datePattern = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
14
+ const datePatternString = "\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])";
13
15
  const createNewDate = (dateString) => {
14
16
  return new Date(dateString + "T00:00:00Z");
15
17
  };
@@ -25,7 +27,17 @@ const getPrevMonthYear = (date) => {
25
27
  const getNextMonthYear = (date) => {
26
28
  return `${getSweMonth(addMonths(date, 1))} ${getSweYear(addMonths(date, 1))}`;
27
29
  };
28
- function IDSDatePicker({ label, id, value, light = false, placeholder = "åååå-mm-dd", errorMsg = "", missingDateErrorMsg = "Datum saknas", invalidDateErrorMsg = "Ogiltigt datum", calendarHeader = "Välj datum", srOpenText = "Öppna kalendern", srCloseText = "Stäng kalendern", validationOnBlur = false, defaultMonth, startMonth = new Date(1900, 0, 1), endMonth = new Date(2050, 0, 1), noValidation = false, disabled = false, required = false, invalid = false, tooltip, disableNavigation = false, modifiers, focusedDay, onChange, onFocus, onBlur, onOpen, onClose, onDayClick, className, ...props }) {
30
+ const parseDateValue = (value) => {
31
+ if (!datePattern.test(value))
32
+ return undefined;
33
+ const parsedDate = createNewDate(value);
34
+ if (!isValid(parsedDate))
35
+ return undefined;
36
+ const [year, month, day] = value.split("-").map(Number);
37
+ const isSameDate = parsedDate.getUTCFullYear() === year && parsedDate.getUTCMonth() === month - 1 && parsedDate.getUTCDate() === day;
38
+ return isSameDate ? parsedDate : undefined;
39
+ };
40
+ function IDSDatePicker({ label, id, value, light = false, placeholder = "åååå-mm-dd", subtitle, dataTestId, errorMsg = "", missingDateErrorMsg = "Datum saknas", invalidDateErrorMsg = "Ogiltigt datum", calendarHeader = "Välj datum", srOpenText = "Öppna kalendern", srCloseText = "Stäng kalendern", validationOnBlur = false, defaultMonth, startMonth = new Date(1900, 0, 1), endMonth = new Date(2050, 0, 1), noValidation = false, disabled = false, required, invalid = false, readOnly = false, tooltip, disableNavigation = false, modifiers, focusedDay, onChange, onFocus, onBlur, onOpen, onClose, onDayClick, className, ...props }) {
29
41
  const reactId = useId();
30
42
  const dialogId = `datepicker-dialog-${reactId}`;
31
43
  const headerId = `datepicker-header-${reactId}`;
@@ -38,24 +50,82 @@ function IDSDatePicker({ label, id, value, light = false, placeholder = "ååå
38
50
  const headerRef = useRef(null);
39
51
  const prevMonthButtonRef = useRef(null);
40
52
  const nextMonthButtonRef = useRef(null);
53
+ const wasValidationEnabledRef = useRef(false);
54
+ const shouldRunRequiredValidation = required === true && !noValidation && !disabled;
55
+ const shouldShowCustomError = invalid === true && !!errorMsg && !noValidation && !disabled;
41
56
  const [isDialogOpen, setIsDialogOpen] = useState(false);
42
- const [inputValue, setInputValue] = useState(value);
43
- const initialSelectedDate = !!value ? createNewDate(value) : undefined;
57
+ const [inputValue, setInputValue] = useState(value ?? "");
58
+ const initialSelectedDate = value ? parseDateValue(value) : undefined;
44
59
  const [selectedDate, setSelectedDate] = useState(initialSelectedDate || defaultMonth);
45
60
  const [month, setMonth] = useState(initialSelectedDate || defaultMonth || new Date());
46
- const [hasDateError, setHasDateError] = useState(false);
47
- const [hasMissingError, setHasMissingError] = useState(false);
48
- const [hasOtherError, setHasOtherError] = useState(false);
49
- const hasError = hasMissingError || hasDateError || hasOtherError || invalid;
50
- const showInvalidError = !!hasDateError && !!invalidDateErrorMsg && !noValidation;
51
- const showMissingError = !!hasMissingError && !!missingDateErrorMsg && !noValidation;
52
- const showError = !!invalid && !!errorMsg && !isDialogOpen && !noValidation;
53
- useAriaDescribedBy(inputRef, errorMsgId, invalid, (showError || showMissingError || showInvalidError) && !noValidation);
61
+ const [showValidationErrors, setShowValidationErrors] = useState(false);
62
+ const [validationError, setValidationError] = useState(null);
63
+ const hasValidationError = showValidationErrors && validationError !== null;
64
+ const hasCustomError = shouldShowCustomError;
65
+ const hasError = hasValidationError || hasCustomError;
66
+ const getValidationError = (nextValue) => {
67
+ if (noValidation || disabled)
68
+ return null;
69
+ const trimmedValue = nextValue.trim();
70
+ if (shouldRunRequiredValidation) {
71
+ if (!trimmedValue)
72
+ return "missing";
73
+ if (!parseDateValue(trimmedValue))
74
+ return "invalid";
75
+ }
76
+ if (shouldShowCustomError)
77
+ return "custom";
78
+ return null;
79
+ };
80
+ const getErrorMessage = (error) => {
81
+ if (error === "missing")
82
+ return missingDateErrorMsg;
83
+ if (error === "invalid")
84
+ return invalidDateErrorMsg;
85
+ if (error === "custom")
86
+ return errorMsg;
87
+ return "";
88
+ };
89
+ const syncNativeValidity = (nextValue) => {
90
+ if (!shouldRunRequiredValidation || !inputRef.current)
91
+ return;
92
+ const nextError = getValidationError(nextValue);
93
+ inputRef.current.setCustomValidity(getErrorMessage(nextError));
94
+ };
95
+ const updateVisibleValidation = (nextValue, shouldShow) => {
96
+ if (!shouldRunRequiredValidation) {
97
+ setValidationError(null);
98
+ setShowValidationErrors(false);
99
+ return;
100
+ }
101
+ const nextError = getValidationError(nextValue);
102
+ setValidationError(nextError);
103
+ setShowValidationErrors(shouldShow);
104
+ };
105
+ const resetVisibleValidation = () => {
106
+ setValidationError(null);
107
+ setShowValidationErrors(false);
108
+ };
109
+ useAriaDescribedBy(inputRef, errorMsgId, hasError, hasError);
54
110
  useFocusTrap(dialogRef.current, isDialogOpen);
55
111
  useClickOutside(() => {
56
112
  setIsDialogOpen(false);
57
113
  onClose?.();
58
114
  }, [dialogRef, triggerRef], triggerRef, isDialogOpen);
115
+ useEffect(() => {
116
+ if (!inputRef.current)
117
+ return;
118
+ if (!shouldRunRequiredValidation) {
119
+ if (wasValidationEnabledRef.current) {
120
+ inputRef.current.setCustomValidity("");
121
+ }
122
+ wasValidationEnabledRef.current = false;
123
+ resetVisibleValidation();
124
+ return;
125
+ }
126
+ wasValidationEnabledRef.current = true;
127
+ syncNativeValidity(inputValue);
128
+ }, [shouldRunRequiredValidation, inputValue, invalid, errorMsg, missingDateErrorMsg, invalidDateErrorMsg]);
59
129
  const handleOpenDialog = (e) => {
60
130
  e.preventDefault();
61
131
  setIsDialogOpen(true);
@@ -63,7 +133,7 @@ function IDSDatePicker({ label, id, value, light = false, placeholder = "ååå
63
133
  };
64
134
  const closeDialog = () => {
65
135
  setIsDialogOpen(false);
66
- triggerRef.current.focus();
136
+ triggerRef.current?.focus();
67
137
  onClose?.();
68
138
  };
69
139
  useEffect(() => {
@@ -79,113 +149,78 @@ function IDSDatePicker({ label, id, value, light = false, placeholder = "ååå
79
149
  document.addEventListener("keydown", handleKeyDown);
80
150
  return () => document.removeEventListener("keydown", handleKeyDown);
81
151
  }, [isDialogOpen]);
152
+ const emitValue = (nextValue, parsedDate) => {
153
+ const trimmedValue = nextValue.trim();
154
+ const isMissing = shouldRunRequiredValidation && !trimmedValue;
155
+ const isInvalidDate = shouldRunRequiredValidation && !!trimmedValue && !parsedDate;
156
+ onChange?.({
157
+ value: nextValue,
158
+ valueAsDate: parsedDate,
159
+ invalidDate: isInvalidDate,
160
+ missingDate: isMissing
161
+ });
162
+ };
82
163
  const handleDayPickerSelect = (date) => {
83
- const dateString = format(date, "yyyy-MM-dd");
84
- resetErrors();
85
- if (!date) {
86
- setInputValue("");
87
- setSelectedDate(undefined);
88
- }
89
- else {
90
- setSelectedDate(date);
91
- setInputValue(dateString);
164
+ const nextValue = date ? format(date, "yyyy-MM-dd") : "";
165
+ resetVisibleValidation();
166
+ setInputValue(nextValue);
167
+ setSelectedDate(date);
168
+ if (date) {
169
+ setMonth(date);
92
170
  }
171
+ emitValue(nextValue, date);
93
172
  requestAnimationFrame(() => {
94
- if (inputRef.current) {
95
- inputRef.current.checkValidity();
96
- inputRef.current.dispatchEvent(new Event("input", { bubbles: true }));
97
- inputRef.current.dispatchEvent(new Event("change", { bubbles: true }));
173
+ if (!inputRef.current)
174
+ return;
175
+ if (shouldRunRequiredValidation) {
176
+ syncNativeValidity(nextValue);
98
177
  }
178
+ inputRef.current.dispatchEvent(new Event("input", { bubbles: true }));
179
+ inputRef.current.dispatchEvent(new Event("change", { bubbles: true }));
99
180
  });
100
- emitValue(dateString, date);
101
181
  closeDialog();
102
182
  };
103
- const updateErrors = (validity, isNotADate) => {
104
- if (!noValidation) {
105
- setHasMissingError(validity.valueMissing);
106
- setHasOtherError(!validity.valid);
107
- if (validity.valueMissing) {
108
- setHasDateError(false);
109
- }
110
- else {
111
- setHasDateError(!!isNotADate || validity.patternMismatch);
112
- }
113
- }
114
- };
115
- const resetErrors = () => {
116
- setHasMissingError(false);
117
- setHasDateError(false);
118
- setHasOtherError(false);
119
- };
120
183
  const handleInputChange = (e) => {
121
- resetErrors();
122
- setInputValue(e.currentTarget.value);
123
- const parsedDate = createNewDate(e.currentTarget.value);
124
- setHasDateError(false);
125
- if (isValid(parsedDate)) {
126
- setSelectedDate(parsedDate);
184
+ const nextValue = e.currentTarget.value;
185
+ const parsedDate = parseDateValue(nextValue.trim());
186
+ resetVisibleValidation();
187
+ setInputValue(nextValue);
188
+ setSelectedDate(parsedDate);
189
+ if (parsedDate) {
127
190
  setMonth(parsedDate);
128
191
  }
129
- else {
130
- setHasDateError(true);
131
- setSelectedDate(undefined);
192
+ if (shouldRunRequiredValidation) {
193
+ syncNativeValidity(nextValue);
132
194
  }
133
- emitValue(e.currentTarget.value, parsedDate);
195
+ emitValue(nextValue, parsedDate);
134
196
  };
135
- // if the input value is changed programmatically
136
197
  useEffect(() => {
137
- if (value !== inputValue && inputRef.current) {
138
- resetErrors();
139
- setInputValue(value);
140
- setHasDateError(false);
141
- if (value) {
142
- const parsedDate = createNewDate(value);
143
- if (isValid(parsedDate)) {
144
- setSelectedDate(parsedDate);
145
- setMonth(parsedDate);
146
- }
147
- else {
148
- setSelectedDate(undefined);
149
- setHasDateError(true);
150
- }
151
- }
198
+ if (value === inputValue)
199
+ return;
200
+ const nextValue = value ?? "";
201
+ const parsedDate = nextValue ? parseDateValue(nextValue.trim()) : undefined;
202
+ resetVisibleValidation();
203
+ setInputValue(nextValue);
204
+ setSelectedDate(parsedDate);
205
+ if (parsedDate) {
206
+ setMonth(parsedDate);
152
207
  }
153
- }, [value, inputRef]);
154
- const emitValue = (val, parsedDate) => {
155
- const isMissing = required && !val;
156
- const isValidDate = parsedDate && parsedDate instanceof Date && isValid(parsedDate);
157
- onChange?.({
158
- value: val,
159
- valueAsDate: isValidDate ? createNewDate(val) : undefined,
160
- invalidDate: !isValid(parsedDate),
161
- missingDate: isMissing
162
- });
163
- };
208
+ }, [value]);
164
209
  const handleInvalid = (e) => {
165
- updateErrors(e.currentTarget.validity);
210
+ if (!shouldRunRequiredValidation)
211
+ return;
212
+ e.preventDefault();
213
+ syncNativeValidity(e.currentTarget.value);
214
+ updateVisibleValidation(e.currentTarget.value, true);
166
215
  };
167
216
  const handleBlur = (e) => {
168
- if (validationOnBlur) {
169
- updateErrors(e.currentTarget.validity, !!e.currentTarget.value.trim().length && !isValid(selectedDate));
217
+ if (shouldRunRequiredValidation && validationOnBlur) {
218
+ syncNativeValidity(e.currentTarget.value);
219
+ updateVisibleValidation(e.currentTarget.value, true);
170
220
  }
171
221
  onBlur?.(e);
172
222
  };
173
- useEffect(() => {
174
- const form = inputRef.current?.form;
175
- if (!form)
176
- return;
177
- const handleSubmit = (_e) => {
178
- if (!noValidation) {
179
- requestAnimationFrame(() => {
180
- updateErrors(inputRef.current.validity, !isValid(createNewDate(inputRef.current.value)));
181
- });
182
- }
183
- };
184
- form.addEventListener("submit", handleSubmit);
185
- return () => form.removeEventListener("submit", handleSubmit);
186
- }, []);
187
223
  function CustomNav(props) {
188
- // Add the nav buttons after the dropdowns for correct tab order
189
224
  const { children } = props;
190
225
  const { goToMonth, previousMonth, nextMonth } = useDayPicker();
191
226
  const currentMonth = "aktuell månad";
@@ -193,28 +228,25 @@ function IDSDatePicker({ label, id, value, light = false, placeholder = "ååå
193
228
  const goToPrevMonth = () => {
194
229
  previousMonth && goToMonth(previousMonth);
195
230
  requestAnimationFrame(() => {
196
- if (prevMonthButtonRef.current) {
197
- prevMonthButtonRef.current.focus();
198
- }
231
+ prevMonthButtonRef.current?.focus();
199
232
  });
200
233
  };
201
234
  const goToNextMonth = () => {
202
235
  nextMonth && goToMonth(nextMonth);
203
236
  requestAnimationFrame(() => {
204
- if (nextMonthButtonRef.current) {
205
- nextMonthButtonRef.current.focus();
206
- }
237
+ nextMonthButtonRef.current?.focus();
207
238
  });
208
239
  };
209
240
  return (jsxs(DropdownNav, { className: "ids-datepicker__nav", ...props, children: [jsx("div", { className: "ids-datepicker__nav-dropdowns", children: children }), jsxs("div", { className: "ids-datepicker__nav-buttons", children: [jsx("button", { type: "button", ref: prevMonthButtonRef, className: "ids-datepicker__nav-prev", onClick: goToPrevMonth, disabled: !previousMonth || disableNavigation, "aria-label": `${currentMonth} ${getSweMonth(month)}. ${goTo} ${getPrevMonthYear(month)}` }), jsx("button", { type: "button", ref: nextMonthButtonRef, className: "ids-datepicker__nav-next", onClick: goToNextMonth, disabled: !nextMonth || disableNavigation, "aria-label": `${currentMonth} ${getSweMonth(month)}. ${goTo} ${getNextMonthYear(month)}` })] })] }));
210
241
  }
211
242
  useEffect(() => {
212
- const header = headerRef.current;
213
- if (!header)
243
+ if (!isDialogOpen)
214
244
  return;
215
- header?.focus();
245
+ headerRef.current?.focus();
216
246
  }, [isDialogOpen]);
217
- return (jsxs("div", { className: clsx("ids-datepicker", { "ids-datepicker--invalid": !noValidation && hasError }, className), ...props, children: [isDialogOpen && jsx("div", { className: "ids-datepicker__overlay" }), label && (jsxs("div", { className: "ids-label-wrapper ids-label-wrapper--margin-bottom", children: [jsx("label", { htmlFor: inputId, className: "ids-label", children: label }), tooltip && jsx("span", { className: "ids-label__tooltip", children: tooltip })] })), jsxs("div", { className: "ids-datepicker__input-wrapper", children: [jsx("input", { className: clsx("ids-datepicker__input", { "ids-input--light": light }), ref: inputRef, style: { fontSize: "inherit" }, id: inputId, type: "text", value: inputValue, required: required, disabled: disabled, "aria-invalid": !noValidation && hasError, placeholder: placeholder, onChange: handleInputChange, onInvalid: handleInvalid, onBlur: handleBlur, pattern: "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$" }), jsx("button", { ref: triggerRef, type: "button", className: "ids-datepicker__trigger", style: { fontSize: "inherit" }, disabled: disabled, onClick: handleOpenDialog, "aria-controls": "dialog", "aria-haspopup": "dialog", "aria-expanded": isDialogOpen, "aria-label": srOpenText }), jsxs("div", { className: clsx("ids-datepicker__dialog", { "ids-datepicker__dialog--show": isDialogOpen }), role: "dialog", ref: dialogRef, id: dialogId, "aria-modal": true, "aria-labelledby": headerId, children: [jsxs("div", { className: "ids-datepicker__dialog-bar", children: [jsx("div", { className: "ids-datepicker__dialog-header", id: headerId, ref: headerRef, tabIndex: -1, children: calendarHeader }), jsx("button", { className: "ids-datepicker__dialog-close-button", type: "button", onClick: closeDialog, "aria-label": srCloseText })] }), jsx(DayPicker, { mode: "single", locale: sv, labels: {
247
+ const visibleErrorType = hasValidationError ? validationError : hasCustomError ? "custom" : null;
248
+ const visibleErrorMessage = visibleErrorType ? getErrorMessage(visibleErrorType) : "";
249
+ return (jsxs("div", { className: clsx("ids-datepicker", { "ids-datepicker--invalid": hasError }, className), "data-testid": dataTestId, children: [isDialogOpen && jsx("div", { className: "ids-datepicker__overlay" }), label && (jsxs("div", { className: "ids-label-wrapper ids-label-wrapper--margin-bottom", children: [jsx("label", { htmlFor: inputId, className: clsx("ids-label", { "ids-label--disabled": disabled }), children: label }), tooltip && jsx("span", { className: "ids-label__tooltip", children: tooltip })] })), subtitle && jsx("div", { className: clsx("ids-subtitle", { "ids-subtitle--disabled": disabled }), children: subtitle }), jsxs("div", { className: "ids-datepicker__input-wrapper", children: [jsx("input", { className: clsx("ids-datepicker__input", { "ids-input--light": light }), ref: inputRef, style: { fontSize: "inherit" }, id: inputId, type: "text", value: inputValue, required: shouldRunRequiredValidation || undefined, pattern: shouldRunRequiredValidation ? datePatternString : undefined, "aria-invalid": hasError || undefined, disabled: disabled, readOnly: readOnly, placeholder: placeholder, onChange: handleInputChange, onFocus: onFocus, onInvalid: handleInvalid, onBlur: handleBlur, ...props }), jsx("button", { ref: triggerRef, type: "button", className: "ids-datepicker__trigger", style: { fontSize: "inherit" }, disabled: disabled || readOnly, onClick: handleOpenDialog, "aria-controls": dialogId, "aria-haspopup": "dialog", "aria-expanded": isDialogOpen, "aria-label": srOpenText }), jsxs("div", { className: clsx("ids-datepicker__dialog", { "ids-datepicker__dialog--show": isDialogOpen }), role: "dialog", ref: dialogRef, id: dialogId, "aria-modal": true, "aria-labelledby": headerId, children: [jsxs("div", { className: "ids-datepicker__dialog-bar", children: [jsx("div", { className: "ids-datepicker__dialog-header", id: headerId, ref: headerRef, tabIndex: -1, children: calendarHeader }), jsx("button", { className: "ids-datepicker__dialog-close-button", type: "button", onClick: closeDialog, "aria-label": srCloseText })] }), jsx(DayPicker, { mode: "single", locale: sv, labels: {
218
250
  labelWeekNumberHeader: () => "Veckonumer",
219
251
  labelWeekNumber: (_weekNumber) => `vecka`,
220
252
  labelDayButton(date, _modifiers, _options, dateLib) {
@@ -235,7 +267,7 @@ function IDSDatePicker({ label, id, value, light = false, placeholder = "ååå
235
267
  WeekNumberHeader: props => (jsx(WeekNumberHeader, { ...props, className: "ids-datepicker__week-number-header", children: shortWeek })),
236
268
  MonthsDropdown: props => (jsx(MonthsDropdown, { ...props, disabled: disableNavigation, className: "ids-datepicker__month-select" })),
237
269
  YearsDropdown: props => (jsx(YearsDropdown, { ...props, disabled: disableNavigation, className: "ids-datepicker__year-select" }))
238
- }, startMonth: startMonth, endMonth: endMonth, month: month, onMonthChange: setMonth, defaultMonth: defaultMonth, selected: selectedDate, onSelect: handleDayPickerSelect, onDayClick: onDayClick })] })] }), (showError || showInvalidError || showMissingError) && (jsxs(IDSErrorMessage, { id: errorMsgId, show: true, children: [showInvalidError && !showMissingError && !showError && invalidDateErrorMsg, showMissingError && !showInvalidError && !showError && missingDateErrorMsg, showError && errorMsg] }))] }));
270
+ }, startMonth: startMonth, endMonth: endMonth, month: month, onMonthChange: setMonth, defaultMonth: defaultMonth, selected: selectedDate, onSelect: handleDayPickerSelect, onDayClick: onDayClick })] })] }), visibleErrorMessage && (jsx(IDSErrorMessage, { id: errorMsgId, show: true, children: visibleErrorMessage }))] }));
239
271
  }
240
272
  IDSDatePicker.displayName = "IDSDatePicker";
241
273
 
@@ -1 +1 @@
1
- export declare function useInputValidity(ref: React.RefObject<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>, validateOnBlur?: boolean): boolean;
1
+ export declare function useInputValidity(ref: React.RefObject<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>, validateOnBlur?: boolean, skipValidation?: boolean): boolean;