@simplybusiness/mobius-datepicker 4.3.32 → 4.3.33

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.3.33
4
+
5
+ ### Patch Changes
6
+
7
+ - 2392b6b: Fix handling of `isInvalid` prop; remove internal use of `validationState`
8
+ - Updated dependencies [2392b6b]
9
+ - Updated dependencies [09bc11f]
10
+ - @simplybusiness/mobius@4.8.8
11
+
3
12
  ## 4.3.32
4
13
 
5
14
  ### Patch Changes
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "DatePicker", {
11
11
  });
12
12
  const _jsxruntime = require("react/jsx-runtime");
13
13
  const _mobius = require("@simplybusiness/mobius");
14
+ const _icons = require("@simplybusiness/icons");
14
15
  const _dedupe = /*#__PURE__*/ _interop_require_default(require("classnames/dedupe"));
15
16
  const _react = require("react");
16
17
  const _isTouchDevice = require("../../utils/isTouchDevice");
@@ -62,29 +63,39 @@ function _interop_require_wildcard(obj, nodeInterop) {
62
63
  return newObj;
63
64
  }
64
65
  const DatePickerModal = /*#__PURE__*/ (0, _react.lazy)(()=>Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("./DatePickerModal"))));
66
+ const getValidationState = (props)=>{
67
+ const { validationState, isInvalid } = props;
68
+ if (isInvalid || validationState === "invalid") {
69
+ return true;
70
+ }
71
+ if (isInvalid === false || validationState === "valid") {
72
+ return false;
73
+ }
74
+ return undefined;
75
+ };
65
76
  const DatePicker = (props)=>{
66
- const { onChange, defaultValue = "", isDisabled, validationState, errorMessage = "", ...otherProps } = props;
77
+ const { onChange, defaultValue = "", isDisabled, validationState, isInvalid, errorMessage = "", ...otherProps } = props;
67
78
  const containerRef = (0, _react.useRef)(null);
68
79
  const [top, setTop] = (0, _react.useState)(0);
69
80
  const inputRef = (0, _react.useRef)(null);
70
81
  const [isOpen, setIsOpen] = (0, _react.useState)(false);
71
82
  const [textFieldVal, setTextFieldVal] = (0, _react.useState)((0, _utils.validateDateFormat)(defaultValue));
72
- const [isValid, setIsValid] = (0, _react.useState)(true);
73
- const errorMessageText = !isValid ? (0, _utils.formatErrorMessageText)(textFieldVal, props.min, props.max) : errorMessage;
74
- const inputValidationState = !isValid ? "invalid" : validationState;
83
+ const [isValid, setIsValid] = (0, _react.useState)(undefined);
84
+ const isInvalidProp = getValidationState({
85
+ validationState,
86
+ isInvalid: isValid === false || isInvalid
87
+ });
88
+ const errorMessageText = isInvalidProp ? (0, _utils.formatErrorMessageText)(textFieldVal, props.min, props.max) : errorMessage;
75
89
  const touchDevice = (0, _isTouchDevice.isTouchDevice)();
76
- const validationClasses = {
77
- "--is-valid": validationState === "valid",
78
- "--is-invalid": validationState === "invalid" || !isValid
79
- };
90
+ const validationClasses = (0, _mobius.useValidationClasses)({
91
+ validationState,
92
+ isInvalid: isInvalidProp
93
+ });
80
94
  const containerClasses = (0, _dedupe.default)("mobius/DatePickerContainer", {
81
95
  "--is-disabled": isDisabled,
82
- "--is-touch-device": touchDevice,
83
- ...validationClasses
84
- });
85
- const popoverToggleClasses = (0, _dedupe.default)("mobius/DateFieldButton", {
86
- ...validationClasses
87
- });
96
+ "--is-touch-device": touchDevice
97
+ }, validationClasses);
98
+ const popoverToggleClasses = (0, _dedupe.default)("mobius/DateFieldButton", validationClasses);
88
99
  const togglePopoverVisibility = ()=>{
89
100
  setIsValid(true);
90
101
  setIsOpen(!isOpen);
@@ -148,7 +159,7 @@ const DatePicker = (props)=>{
148
159
  onChange: handleTextFieldChange,
149
160
  value: textFieldVal,
150
161
  isDisabled: isDisabled,
151
- validationState: inputValidationState,
162
+ isInvalid: isInvalidProp,
152
163
  ...otherProps,
153
164
  errorMessage: errorMessageText
154
165
  })
@@ -167,16 +178,22 @@ const DatePicker = (props)=>{
167
178
  onChange: handleTextFieldChange,
168
179
  value: textFieldVal,
169
180
  isDisabled: isDisabled,
170
- validationState: inputValidationState,
181
+ isInvalid: isInvalidProp,
171
182
  ...otherProps,
172
183
  errorMessage: errorMessageText,
173
- suffixOutside: /*#__PURE__*/ (0, _jsxruntime.jsx)(_mobius.Button, {
184
+ suffixOutside: /*#__PURE__*/ (0, _jsxruntime.jsxs)(_mobius.Button, {
174
185
  className: popoverToggleClasses,
175
186
  onClick: togglePopoverVisibility,
176
187
  isDisabled: isDisabled,
177
- children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_mobius.VisuallyHidden, {
178
- children: "Pick date"
179
- })
188
+ children: [
189
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_mobius.Icon, {
190
+ size: "sm",
191
+ icon: _icons.calendarDay
192
+ }),
193
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_mobius.VisuallyHidden, {
194
+ children: "Pick date"
195
+ })
196
+ ]
180
197
  })
181
198
  }),
182
199
  isOpen && /*#__PURE__*/ (0, _jsxruntime.jsx)(_react.Suspense, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n Button,\n TextField,\n TextFieldElementType,\n TextFieldProps,\n VisuallyHidden,\n} from \"@simplybusiness/mobius\";\nimport classNames from \"classnames/dedupe\";\nimport {\n ChangeEvent,\n FocusEvent,\n Suspense,\n lazy,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { isTouchDevice } from \"../../utils/isTouchDevice\";\nimport { formatErrorMessageText, validateDateFormat } from \"./utils\";\n\nconst DatePickerModal = lazy(() => import(\"./DatePickerModal\"));\n\nexport interface DatePickerProps\n extends Omit<\n TextFieldProps,\n \"defaultValue\" | \"onChange\" | \"onBlur\" | \"onFocus\"\n > {\n onChange?: (date: string | undefined) => void;\n defaultValue?: string;\n min?: string;\n max?: string;\n id?: string;\n}\n\nexport const DatePicker = (props: DatePickerProps) => {\n const {\n onChange,\n defaultValue = \"\",\n isDisabled,\n validationState,\n errorMessage = \"\",\n ...otherProps\n } = props;\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [top, setTop] = useState<number>(0);\n const inputRef = useRef<TextFieldElementType | null>(null);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n const [textFieldVal, setTextFieldVal] = useState<string>(\n validateDateFormat(defaultValue),\n );\n const [isValid, setIsValid] = useState<boolean>(true);\n const errorMessageText = !isValid\n ? // eslint-disable-next-line react/destructuring-assignment\n formatErrorMessageText(textFieldVal, props.min, props.max)\n : errorMessage;\n const inputValidationState = !isValid ? \"invalid\" : validationState;\n const touchDevice = isTouchDevice();\n\n const validationClasses = {\n \"--is-valid\": validationState === \"valid\",\n \"--is-invalid\": validationState === \"invalid\" || !isValid,\n };\n\n const containerClasses = classNames(\"mobius/DatePickerContainer\", {\n \"--is-disabled\": isDisabled,\n \"--is-touch-device\": touchDevice,\n ...validationClasses,\n });\n\n const popoverToggleClasses = classNames(\"mobius/DateFieldButton\", {\n ...validationClasses,\n });\n\n const togglePopoverVisibility = () => {\n setIsValid(true);\n setIsOpen(!isOpen);\n };\n\n const handleTextFieldChange = (event: ChangeEvent<TextFieldElementType>) => {\n setTextFieldVal(event.target.value);\n // onChange only triggers on a valid date\n // so this clears the error\n setIsValid(true);\n };\n\n const validate = () => {\n // If 'min' or 'max' values are provided, checkValidity() will\n // validate the date and return a boolean\n const isValidInput = inputRef.current?.checkValidity();\n\n if (!isValidInput) {\n setIsValid(false);\n }\n };\n\n const onDateSelected = (selectedDate: string | undefined) => {\n // Handle null callback from useOnClickOutside\n if (selectedDate) {\n setTextFieldVal(selectedDate);\n setIsValid(true);\n // Add other callback events here\n onChange?.(selectedDate);\n }\n\n setIsOpen(false);\n };\n\n // User has interacted with the component and navigated away\n const handleBlur = (event: FocusEvent<TextFieldElementType>) => {\n validate();\n\n // User hasn't entered a date OR\n // entered an invalid date\n if (!textFieldVal) {\n setIsValid(false);\n }\n\n onChange?.(event.target.value);\n };\n\n useEffect(() => {\n if (isOpen) {\n setTop(containerRef.current?.getBoundingClientRect().height || 0);\n // Disable validation when day picker is open\n setIsValid(true);\n return;\n }\n\n validate();\n }, [isOpen]);\n\n if (touchDevice) {\n return (\n <div className={containerClasses}>\n <TextField\n ref={inputRef}\n type=\"date\"\n className=\"mobius/DatePicker\"\n // @ts-expect-error event type\n onBlur={handleBlur}\n onChange={handleTextFieldChange}\n value={textFieldVal}\n isDisabled={isDisabled}\n validationState={inputValidationState}\n {...otherProps}\n errorMessage={errorMessageText}\n />\n </div>\n );\n }\n\n return (\n <div className={containerClasses} ref={containerRef}>\n <TextField\n ref={inputRef}\n type=\"date\"\n className=\"mobius/DatePicker\"\n // @ts-expect-error event type\n onBlur={handleBlur}\n onChange={handleTextFieldChange}\n value={textFieldVal}\n isDisabled={isDisabled}\n validationState={inputValidationState}\n {...otherProps}\n errorMessage={errorMessageText}\n suffixOutside={\n <Button\n className={popoverToggleClasses}\n onClick={togglePopoverVisibility}\n isDisabled={isDisabled}\n >\n <VisuallyHidden>Pick date</VisuallyHidden>\n </Button>\n }\n />\n {isOpen && (\n <Suspense>\n <DatePickerModal\n date={textFieldVal}\n isOpen={isOpen}\n top={top}\n onSelected={onDateSelected}\n // eslint-disable-next-line react/destructuring-assignment\n min={props.min}\n // eslint-disable-next-line react/destructuring-assignment\n max={props.max}\n />\n </Suspense>\n )}\n </div>\n );\n};\n"],"names":["DatePicker","DatePickerModal","lazy","props","onChange","defaultValue","isDisabled","validationState","errorMessage","otherProps","containerRef","useRef","top","setTop","useState","inputRef","isOpen","setIsOpen","textFieldVal","setTextFieldVal","validateDateFormat","isValid","setIsValid","errorMessageText","formatErrorMessageText","min","max","inputValidationState","touchDevice","isTouchDevice","validationClasses","containerClasses","classNames","popoverToggleClasses","togglePopoverVisibility","handleTextFieldChange","event","target","value","validate","isValidInput","current","checkValidity","onDateSelected","selectedDate","handleBlur","useEffect","getBoundingClientRect","height","div","className","TextField","ref","type","onBlur","suffixOutside","Button","onClick","VisuallyHidden","Suspense","date","onSelected"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n Button,\n TextField,\n TextFieldElementType,\n TextFieldProps,\n VisuallyHidden,\n Validation,\n useValidationClasses,\n Icon,\n} from \"@simplybusiness/mobius\";\nimport { calendarDay } from \"@simplybusiness/icons\";\nimport classNames from \"classnames/dedupe\";\nimport {\n ChangeEvent,\n FocusEvent,\n Suspense,\n lazy,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { isTouchDevice } from \"../../utils/isTouchDevice\";\nimport { formatErrorMessageText, validateDateFormat } from \"./utils\";\n\nconst DatePickerModal = lazy(() => import(\"./DatePickerModal\"));\n\nexport interface GetValidationStateProps {\n validationState?: Validation[\"validationState\"];\n isInvalid?: Validation[\"isInvalid\"];\n}\n\nconst getValidationState = (props: GetValidationStateProps) => {\n const { validationState, isInvalid } = props;\n\n if (isInvalid || validationState === \"invalid\") {\n return true;\n }\n\n if (isInvalid === false || validationState === \"valid\") {\n return false;\n }\n\n return undefined;\n};\n\nexport interface DatePickerProps\n extends Validation,\n Omit<TextFieldProps, \"defaultValue\" | \"onChange\" | \"onBlur\" | \"onFocus\"> {\n onChange?: (date: string | undefined) => void;\n defaultValue?: string;\n min?: string;\n max?: string;\n id?: string;\n}\n\nexport const DatePicker = (props: DatePickerProps) => {\n const {\n onChange,\n defaultValue = \"\",\n isDisabled,\n validationState,\n isInvalid,\n errorMessage = \"\",\n ...otherProps\n } = props;\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [top, setTop] = useState<number>(0);\n const inputRef = useRef<TextFieldElementType | null>(null);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n const [textFieldVal, setTextFieldVal] = useState<string>(\n validateDateFormat(defaultValue),\n );\n const [isValid, setIsValid] = useState<boolean | undefined>(undefined);\n const isInvalidProp = getValidationState({\n validationState,\n isInvalid: isValid === false || isInvalid,\n });\n const errorMessageText = isInvalidProp\n ? // eslint-disable-next-line react/destructuring-assignment\n formatErrorMessageText(textFieldVal, props.min, props.max)\n : errorMessage;\n const touchDevice = isTouchDevice();\n\n const validationClasses = useValidationClasses({\n validationState,\n isInvalid: isInvalidProp,\n });\n\n const containerClasses = classNames(\n \"mobius/DatePickerContainer\",\n {\n \"--is-disabled\": isDisabled,\n \"--is-touch-device\": touchDevice,\n },\n validationClasses,\n );\n\n const popoverToggleClasses = classNames(\n \"mobius/DateFieldButton\",\n validationClasses,\n );\n\n const togglePopoverVisibility = () => {\n setIsValid(true);\n setIsOpen(!isOpen);\n };\n\n const handleTextFieldChange = (event: ChangeEvent<TextFieldElementType>) => {\n setTextFieldVal(event.target.value);\n // onChange only triggers on a valid date\n // so this clears the error\n setIsValid(true);\n };\n\n const validate = () => {\n // If 'min' or 'max' values are provided, checkValidity() will\n // validate the date and return a boolean\n const isValidInput = inputRef.current?.checkValidity();\n\n if (!isValidInput) {\n setIsValid(false);\n }\n };\n\n const onDateSelected = (selectedDate: string | undefined) => {\n // Handle null callback from useOnClickOutside\n if (selectedDate) {\n setTextFieldVal(selectedDate);\n setIsValid(true);\n // Add other callback events here\n onChange?.(selectedDate);\n }\n\n setIsOpen(false);\n };\n\n // User has interacted with the component and navigated away\n const handleBlur = (event: FocusEvent<TextFieldElementType>) => {\n validate();\n\n // User hasn't entered a date OR\n // entered an invalid date\n if (!textFieldVal) {\n setIsValid(false);\n }\n\n onChange?.(event.target.value);\n };\n\n useEffect(() => {\n if (isOpen) {\n setTop(containerRef.current?.getBoundingClientRect().height || 0);\n // Disable validation when day picker is open\n setIsValid(true);\n return;\n }\n\n validate();\n }, [isOpen]);\n\n if (touchDevice) {\n return (\n <div className={containerClasses}>\n <TextField\n ref={inputRef}\n type=\"date\"\n className=\"mobius/DatePicker\"\n // @ts-expect-error event type\n onBlur={handleBlur}\n onChange={handleTextFieldChange}\n value={textFieldVal}\n isDisabled={isDisabled}\n isInvalid={isInvalidProp}\n {...otherProps}\n errorMessage={errorMessageText}\n />\n </div>\n );\n }\n\n return (\n <div className={containerClasses} ref={containerRef}>\n <TextField\n ref={inputRef}\n type=\"date\"\n className=\"mobius/DatePicker\"\n // @ts-expect-error event type\n onBlur={handleBlur}\n onChange={handleTextFieldChange}\n value={textFieldVal}\n isDisabled={isDisabled}\n isInvalid={isInvalidProp}\n {...otherProps}\n errorMessage={errorMessageText}\n suffixOutside={\n <Button\n className={popoverToggleClasses}\n onClick={togglePopoverVisibility}\n isDisabled={isDisabled}\n >\n <Icon size=\"sm\" icon={calendarDay} />\n <VisuallyHidden>Pick date</VisuallyHidden>\n </Button>\n }\n />\n {isOpen && (\n <Suspense>\n <DatePickerModal\n date={textFieldVal}\n isOpen={isOpen}\n top={top}\n onSelected={onDateSelected}\n // eslint-disable-next-line react/destructuring-assignment\n min={props.min}\n // eslint-disable-next-line react/destructuring-assignment\n max={props.max}\n />\n </Suspense>\n )}\n </div>\n );\n};\n"],"names":["DatePicker","DatePickerModal","lazy","getValidationState","props","validationState","isInvalid","undefined","onChange","defaultValue","isDisabled","errorMessage","otherProps","containerRef","useRef","top","setTop","useState","inputRef","isOpen","setIsOpen","textFieldVal","setTextFieldVal","validateDateFormat","isValid","setIsValid","isInvalidProp","errorMessageText","formatErrorMessageText","min","max","touchDevice","isTouchDevice","validationClasses","useValidationClasses","containerClasses","classNames","popoverToggleClasses","togglePopoverVisibility","handleTextFieldChange","event","target","value","validate","isValidInput","current","checkValidity","onDateSelected","selectedDate","handleBlur","useEffect","getBoundingClientRect","height","div","className","TextField","ref","type","onBlur","suffixOutside","Button","onClick","Icon","size","icon","calendarDay","VisuallyHidden","Suspense","date","onSelected"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -1,34 +1,45 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Button, TextField, VisuallyHidden } from "@simplybusiness/mobius";
3
+ import { Button, TextField, VisuallyHidden, useValidationClasses, Icon } from "@simplybusiness/mobius";
4
+ import { calendarDay } from "@simplybusiness/icons";
4
5
  import classNames from "classnames/dedupe";
5
6
  import { Suspense, lazy, useEffect, useRef, useState } from "react";
6
7
  import { isTouchDevice } from "../../utils/isTouchDevice";
7
8
  import { formatErrorMessageText, validateDateFormat } from "./utils";
8
9
  const DatePickerModal = /*#__PURE__*/ lazy(()=>import("./DatePickerModal"));
10
+ const getValidationState = (props)=>{
11
+ const { validationState, isInvalid } = props;
12
+ if (isInvalid || validationState === "invalid") {
13
+ return true;
14
+ }
15
+ if (isInvalid === false || validationState === "valid") {
16
+ return false;
17
+ }
18
+ return undefined;
19
+ };
9
20
  export const DatePicker = (props)=>{
10
- const { onChange, defaultValue = "", isDisabled, validationState, errorMessage = "", ...otherProps } = props;
21
+ const { onChange, defaultValue = "", isDisabled, validationState, isInvalid, errorMessage = "", ...otherProps } = props;
11
22
  const containerRef = useRef(null);
12
23
  const [top, setTop] = useState(0);
13
24
  const inputRef = useRef(null);
14
25
  const [isOpen, setIsOpen] = useState(false);
15
26
  const [textFieldVal, setTextFieldVal] = useState(validateDateFormat(defaultValue));
16
- const [isValid, setIsValid] = useState(true);
17
- const errorMessageText = !isValid ? formatErrorMessageText(textFieldVal, props.min, props.max) : errorMessage;
18
- const inputValidationState = !isValid ? "invalid" : validationState;
27
+ const [isValid, setIsValid] = useState(undefined);
28
+ const isInvalidProp = getValidationState({
29
+ validationState,
30
+ isInvalid: isValid === false || isInvalid
31
+ });
32
+ const errorMessageText = isInvalidProp ? formatErrorMessageText(textFieldVal, props.min, props.max) : errorMessage;
19
33
  const touchDevice = isTouchDevice();
20
- const validationClasses = {
21
- "--is-valid": validationState === "valid",
22
- "--is-invalid": validationState === "invalid" || !isValid
23
- };
34
+ const validationClasses = useValidationClasses({
35
+ validationState,
36
+ isInvalid: isInvalidProp
37
+ });
24
38
  const containerClasses = classNames("mobius/DatePickerContainer", {
25
39
  "--is-disabled": isDisabled,
26
- "--is-touch-device": touchDevice,
27
- ...validationClasses
28
- });
29
- const popoverToggleClasses = classNames("mobius/DateFieldButton", {
30
- ...validationClasses
31
- });
40
+ "--is-touch-device": touchDevice
41
+ }, validationClasses);
42
+ const popoverToggleClasses = classNames("mobius/DateFieldButton", validationClasses);
32
43
  const togglePopoverVisibility = ()=>{
33
44
  setIsValid(true);
34
45
  setIsOpen(!isOpen);
@@ -92,7 +103,7 @@ export const DatePicker = (props)=>{
92
103
  onChange: handleTextFieldChange,
93
104
  value: textFieldVal,
94
105
  isDisabled: isDisabled,
95
- validationState: inputValidationState,
106
+ isInvalid: isInvalidProp,
96
107
  ...otherProps,
97
108
  errorMessage: errorMessageText
98
109
  })
@@ -111,16 +122,22 @@ export const DatePicker = (props)=>{
111
122
  onChange: handleTextFieldChange,
112
123
  value: textFieldVal,
113
124
  isDisabled: isDisabled,
114
- validationState: inputValidationState,
125
+ isInvalid: isInvalidProp,
115
126
  ...otherProps,
116
127
  errorMessage: errorMessageText,
117
- suffixOutside: /*#__PURE__*/ _jsx(Button, {
128
+ suffixOutside: /*#__PURE__*/ _jsxs(Button, {
118
129
  className: popoverToggleClasses,
119
130
  onClick: togglePopoverVisibility,
120
131
  isDisabled: isDisabled,
121
- children: /*#__PURE__*/ _jsx(VisuallyHidden, {
122
- children: "Pick date"
123
- })
132
+ children: [
133
+ /*#__PURE__*/ _jsx(Icon, {
134
+ size: "sm",
135
+ icon: calendarDay
136
+ }),
137
+ /*#__PURE__*/ _jsx(VisuallyHidden, {
138
+ children: "Pick date"
139
+ })
140
+ ]
124
141
  })
125
142
  }),
126
143
  isOpen && /*#__PURE__*/ _jsx(Suspense, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n Button,\n TextField,\n TextFieldElementType,\n TextFieldProps,\n VisuallyHidden,\n} from \"@simplybusiness/mobius\";\nimport classNames from \"classnames/dedupe\";\nimport {\n ChangeEvent,\n FocusEvent,\n Suspense,\n lazy,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { isTouchDevice } from \"../../utils/isTouchDevice\";\nimport { formatErrorMessageText, validateDateFormat } from \"./utils\";\n\nconst DatePickerModal = lazy(() => import(\"./DatePickerModal\"));\n\nexport interface DatePickerProps\n extends Omit<\n TextFieldProps,\n \"defaultValue\" | \"onChange\" | \"onBlur\" | \"onFocus\"\n > {\n onChange?: (date: string | undefined) => void;\n defaultValue?: string;\n min?: string;\n max?: string;\n id?: string;\n}\n\nexport const DatePicker = (props: DatePickerProps) => {\n const {\n onChange,\n defaultValue = \"\",\n isDisabled,\n validationState,\n errorMessage = \"\",\n ...otherProps\n } = props;\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [top, setTop] = useState<number>(0);\n const inputRef = useRef<TextFieldElementType | null>(null);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n const [textFieldVal, setTextFieldVal] = useState<string>(\n validateDateFormat(defaultValue),\n );\n const [isValid, setIsValid] = useState<boolean>(true);\n const errorMessageText = !isValid\n ? // eslint-disable-next-line react/destructuring-assignment\n formatErrorMessageText(textFieldVal, props.min, props.max)\n : errorMessage;\n const inputValidationState = !isValid ? \"invalid\" : validationState;\n const touchDevice = isTouchDevice();\n\n const validationClasses = {\n \"--is-valid\": validationState === \"valid\",\n \"--is-invalid\": validationState === \"invalid\" || !isValid,\n };\n\n const containerClasses = classNames(\"mobius/DatePickerContainer\", {\n \"--is-disabled\": isDisabled,\n \"--is-touch-device\": touchDevice,\n ...validationClasses,\n });\n\n const popoverToggleClasses = classNames(\"mobius/DateFieldButton\", {\n ...validationClasses,\n });\n\n const togglePopoverVisibility = () => {\n setIsValid(true);\n setIsOpen(!isOpen);\n };\n\n const handleTextFieldChange = (event: ChangeEvent<TextFieldElementType>) => {\n setTextFieldVal(event.target.value);\n // onChange only triggers on a valid date\n // so this clears the error\n setIsValid(true);\n };\n\n const validate = () => {\n // If 'min' or 'max' values are provided, checkValidity() will\n // validate the date and return a boolean\n const isValidInput = inputRef.current?.checkValidity();\n\n if (!isValidInput) {\n setIsValid(false);\n }\n };\n\n const onDateSelected = (selectedDate: string | undefined) => {\n // Handle null callback from useOnClickOutside\n if (selectedDate) {\n setTextFieldVal(selectedDate);\n setIsValid(true);\n // Add other callback events here\n onChange?.(selectedDate);\n }\n\n setIsOpen(false);\n };\n\n // User has interacted with the component and navigated away\n const handleBlur = (event: FocusEvent<TextFieldElementType>) => {\n validate();\n\n // User hasn't entered a date OR\n // entered an invalid date\n if (!textFieldVal) {\n setIsValid(false);\n }\n\n onChange?.(event.target.value);\n };\n\n useEffect(() => {\n if (isOpen) {\n setTop(containerRef.current?.getBoundingClientRect().height || 0);\n // Disable validation when day picker is open\n setIsValid(true);\n return;\n }\n\n validate();\n }, [isOpen]);\n\n if (touchDevice) {\n return (\n <div className={containerClasses}>\n <TextField\n ref={inputRef}\n type=\"date\"\n className=\"mobius/DatePicker\"\n // @ts-expect-error event type\n onBlur={handleBlur}\n onChange={handleTextFieldChange}\n value={textFieldVal}\n isDisabled={isDisabled}\n validationState={inputValidationState}\n {...otherProps}\n errorMessage={errorMessageText}\n />\n </div>\n );\n }\n\n return (\n <div className={containerClasses} ref={containerRef}>\n <TextField\n ref={inputRef}\n type=\"date\"\n className=\"mobius/DatePicker\"\n // @ts-expect-error event type\n onBlur={handleBlur}\n onChange={handleTextFieldChange}\n value={textFieldVal}\n isDisabled={isDisabled}\n validationState={inputValidationState}\n {...otherProps}\n errorMessage={errorMessageText}\n suffixOutside={\n <Button\n className={popoverToggleClasses}\n onClick={togglePopoverVisibility}\n isDisabled={isDisabled}\n >\n <VisuallyHidden>Pick date</VisuallyHidden>\n </Button>\n }\n />\n {isOpen && (\n <Suspense>\n <DatePickerModal\n date={textFieldVal}\n isOpen={isOpen}\n top={top}\n onSelected={onDateSelected}\n // eslint-disable-next-line react/destructuring-assignment\n min={props.min}\n // eslint-disable-next-line react/destructuring-assignment\n max={props.max}\n />\n </Suspense>\n )}\n </div>\n );\n};\n"],"names":["Button","TextField","VisuallyHidden","classNames","Suspense","lazy","useEffect","useRef","useState","isTouchDevice","formatErrorMessageText","validateDateFormat","DatePickerModal","DatePicker","props","onChange","defaultValue","isDisabled","validationState","errorMessage","otherProps","containerRef","top","setTop","inputRef","isOpen","setIsOpen","textFieldVal","setTextFieldVal","isValid","setIsValid","errorMessageText","min","max","inputValidationState","touchDevice","validationClasses","containerClasses","popoverToggleClasses","togglePopoverVisibility","handleTextFieldChange","event","target","value","validate","isValidInput","current","checkValidity","onDateSelected","selectedDate","handleBlur","getBoundingClientRect","height","div","className","ref","type","onBlur","suffixOutside","onClick","date","onSelected"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n Button,\n TextField,\n TextFieldElementType,\n TextFieldProps,\n VisuallyHidden,\n Validation,\n useValidationClasses,\n Icon,\n} from \"@simplybusiness/mobius\";\nimport { calendarDay } from \"@simplybusiness/icons\";\nimport classNames from \"classnames/dedupe\";\nimport {\n ChangeEvent,\n FocusEvent,\n Suspense,\n lazy,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { isTouchDevice } from \"../../utils/isTouchDevice\";\nimport { formatErrorMessageText, validateDateFormat } from \"./utils\";\n\nconst DatePickerModal = lazy(() => import(\"./DatePickerModal\"));\n\nexport interface GetValidationStateProps {\n validationState?: Validation[\"validationState\"];\n isInvalid?: Validation[\"isInvalid\"];\n}\n\nconst getValidationState = (props: GetValidationStateProps) => {\n const { validationState, isInvalid } = props;\n\n if (isInvalid || validationState === \"invalid\") {\n return true;\n }\n\n if (isInvalid === false || validationState === \"valid\") {\n return false;\n }\n\n return undefined;\n};\n\nexport interface DatePickerProps\n extends Validation,\n Omit<TextFieldProps, \"defaultValue\" | \"onChange\" | \"onBlur\" | \"onFocus\"> {\n onChange?: (date: string | undefined) => void;\n defaultValue?: string;\n min?: string;\n max?: string;\n id?: string;\n}\n\nexport const DatePicker = (props: DatePickerProps) => {\n const {\n onChange,\n defaultValue = \"\",\n isDisabled,\n validationState,\n isInvalid,\n errorMessage = \"\",\n ...otherProps\n } = props;\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [top, setTop] = useState<number>(0);\n const inputRef = useRef<TextFieldElementType | null>(null);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n const [textFieldVal, setTextFieldVal] = useState<string>(\n validateDateFormat(defaultValue),\n );\n const [isValid, setIsValid] = useState<boolean | undefined>(undefined);\n const isInvalidProp = getValidationState({\n validationState,\n isInvalid: isValid === false || isInvalid,\n });\n const errorMessageText = isInvalidProp\n ? // eslint-disable-next-line react/destructuring-assignment\n formatErrorMessageText(textFieldVal, props.min, props.max)\n : errorMessage;\n const touchDevice = isTouchDevice();\n\n const validationClasses = useValidationClasses({\n validationState,\n isInvalid: isInvalidProp,\n });\n\n const containerClasses = classNames(\n \"mobius/DatePickerContainer\",\n {\n \"--is-disabled\": isDisabled,\n \"--is-touch-device\": touchDevice,\n },\n validationClasses,\n );\n\n const popoverToggleClasses = classNames(\n \"mobius/DateFieldButton\",\n validationClasses,\n );\n\n const togglePopoverVisibility = () => {\n setIsValid(true);\n setIsOpen(!isOpen);\n };\n\n const handleTextFieldChange = (event: ChangeEvent<TextFieldElementType>) => {\n setTextFieldVal(event.target.value);\n // onChange only triggers on a valid date\n // so this clears the error\n setIsValid(true);\n };\n\n const validate = () => {\n // If 'min' or 'max' values are provided, checkValidity() will\n // validate the date and return a boolean\n const isValidInput = inputRef.current?.checkValidity();\n\n if (!isValidInput) {\n setIsValid(false);\n }\n };\n\n const onDateSelected = (selectedDate: string | undefined) => {\n // Handle null callback from useOnClickOutside\n if (selectedDate) {\n setTextFieldVal(selectedDate);\n setIsValid(true);\n // Add other callback events here\n onChange?.(selectedDate);\n }\n\n setIsOpen(false);\n };\n\n // User has interacted with the component and navigated away\n const handleBlur = (event: FocusEvent<TextFieldElementType>) => {\n validate();\n\n // User hasn't entered a date OR\n // entered an invalid date\n if (!textFieldVal) {\n setIsValid(false);\n }\n\n onChange?.(event.target.value);\n };\n\n useEffect(() => {\n if (isOpen) {\n setTop(containerRef.current?.getBoundingClientRect().height || 0);\n // Disable validation when day picker is open\n setIsValid(true);\n return;\n }\n\n validate();\n }, [isOpen]);\n\n if (touchDevice) {\n return (\n <div className={containerClasses}>\n <TextField\n ref={inputRef}\n type=\"date\"\n className=\"mobius/DatePicker\"\n // @ts-expect-error event type\n onBlur={handleBlur}\n onChange={handleTextFieldChange}\n value={textFieldVal}\n isDisabled={isDisabled}\n isInvalid={isInvalidProp}\n {...otherProps}\n errorMessage={errorMessageText}\n />\n </div>\n );\n }\n\n return (\n <div className={containerClasses} ref={containerRef}>\n <TextField\n ref={inputRef}\n type=\"date\"\n className=\"mobius/DatePicker\"\n // @ts-expect-error event type\n onBlur={handleBlur}\n onChange={handleTextFieldChange}\n value={textFieldVal}\n isDisabled={isDisabled}\n isInvalid={isInvalidProp}\n {...otherProps}\n errorMessage={errorMessageText}\n suffixOutside={\n <Button\n className={popoverToggleClasses}\n onClick={togglePopoverVisibility}\n isDisabled={isDisabled}\n >\n <Icon size=\"sm\" icon={calendarDay} />\n <VisuallyHidden>Pick date</VisuallyHidden>\n </Button>\n }\n />\n {isOpen && (\n <Suspense>\n <DatePickerModal\n date={textFieldVal}\n isOpen={isOpen}\n top={top}\n onSelected={onDateSelected}\n // eslint-disable-next-line react/destructuring-assignment\n min={props.min}\n // eslint-disable-next-line react/destructuring-assignment\n max={props.max}\n />\n </Suspense>\n )}\n </div>\n );\n};\n"],"names":["Button","TextField","VisuallyHidden","useValidationClasses","Icon","calendarDay","classNames","Suspense","lazy","useEffect","useRef","useState","isTouchDevice","formatErrorMessageText","validateDateFormat","DatePickerModal","getValidationState","props","validationState","isInvalid","undefined","DatePicker","onChange","defaultValue","isDisabled","errorMessage","otherProps","containerRef","top","setTop","inputRef","isOpen","setIsOpen","textFieldVal","setTextFieldVal","isValid","setIsValid","isInvalidProp","errorMessageText","min","max","touchDevice","validationClasses","containerClasses","popoverToggleClasses","togglePopoverVisibility","handleTextFieldChange","event","target","value","validate","isValidInput","current","checkValidity","onDateSelected","selectedDate","handleBlur","getBoundingClientRect","height","div","className","ref","type","onBlur","suffixOutside","onClick","size","icon","date","onSelected"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -1,5 +1,9 @@
1
- import { TextFieldProps } from "@simplybusiness/mobius";
2
- export interface DatePickerProps extends Omit<TextFieldProps, "defaultValue" | "onChange" | "onBlur" | "onFocus"> {
1
+ import { TextFieldProps, Validation } from "@simplybusiness/mobius";
2
+ export interface GetValidationStateProps {
3
+ validationState?: Validation["validationState"];
4
+ isInvalid?: Validation["isInvalid"];
5
+ }
6
+ export interface DatePickerProps extends Validation, Omit<TextFieldProps, "defaultValue" | "onChange" | "onBlur" | "onFocus"> {
3
7
  onChange?: (date: string | undefined) => void;
4
8
  defaultValue?: string;
5
9
  min?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@simplybusiness/mobius-datepicker",
3
3
  "license": "UNLICENSED",
4
- "version": "4.3.32",
4
+ "version": "4.3.33",
5
5
  "description": "Mobius date picker component",
6
6
  "repository": {
7
7
  "type": "git",
@@ -73,7 +73,7 @@
73
73
  },
74
74
  "dependencies": {
75
75
  "@simplybusiness/icons": "^4.7.1",
76
- "@simplybusiness/mobius": "^4.8.7",
76
+ "@simplybusiness/mobius": "^4.8.8",
77
77
  "classnames": "^2.5.1",
78
78
  "date-fns": "^3.6.0",
79
79
  "react-day-picker": "^8.10.1"
@@ -10,21 +10,13 @@ const meta: Meta<typeof DatePicker> = {
10
10
  title: "Forms/DatePicker",
11
11
  component: DatePicker,
12
12
  argTypes: {
13
- validationState: {
14
- options: ["valid", "invalid", "neither"],
15
- control: { type: "radio" },
16
- mapping: {
17
- valid: "valid",
18
- invalid: "invalid",
19
- neither: "",
20
- },
21
- },
22
13
  ...excludeControls(
23
14
  "description",
24
15
  "type",
25
16
  "labelElementType",
26
17
  "inputElementType",
27
18
  "isReadOnly",
19
+ "validationState",
28
20
  ),
29
21
  },
30
22
  args: {
@@ -84,7 +76,7 @@ export const Valid: StoryType = {
84
76
  render: (args: DatePickerProps) => <DatePicker {...args} />,
85
77
  args: {
86
78
  label: "Start date",
87
- validationState: "valid",
79
+ isInvalid: false,
88
80
  },
89
81
  };
90
82
 
@@ -92,7 +84,7 @@ export const Invalid: StoryType = {
92
84
  render: (args: DatePickerProps) => <DatePicker {...args} />,
93
85
  args: {
94
86
  label: "Start date",
95
- validationState: "invalid",
87
+ isInvalid: true,
96
88
  errorMessage: "This is an error message",
97
89
  },
98
90
  };
@@ -79,7 +79,7 @@ describe("DatePicker", () => {
79
79
  <DatePicker
80
80
  defaultValue={exampleDate}
81
81
  label={labelText}
82
- validationState="valid"
82
+ isInvalid={false}
83
83
  />,
84
84
  );
85
85
 
@@ -96,7 +96,7 @@ describe("DatePicker", () => {
96
96
  <DatePicker
97
97
  defaultValue={exampleDate}
98
98
  label={labelText}
99
- validationState="invalid"
99
+ isInvalid
100
100
  />,
101
101
  );
102
102
 
@@ -6,7 +6,11 @@ import {
6
6
  TextFieldElementType,
7
7
  TextFieldProps,
8
8
  VisuallyHidden,
9
+ Validation,
10
+ useValidationClasses,
11
+ Icon,
9
12
  } from "@simplybusiness/mobius";
13
+ import { calendarDay } from "@simplybusiness/icons";
10
14
  import classNames from "classnames/dedupe";
11
15
  import {
12
16
  ChangeEvent,
@@ -22,11 +26,28 @@ import { formatErrorMessageText, validateDateFormat } from "./utils";
22
26
 
23
27
  const DatePickerModal = lazy(() => import("./DatePickerModal"));
24
28
 
29
+ export interface GetValidationStateProps {
30
+ validationState?: Validation["validationState"];
31
+ isInvalid?: Validation["isInvalid"];
32
+ }
33
+
34
+ const getValidationState = (props: GetValidationStateProps) => {
35
+ const { validationState, isInvalid } = props;
36
+
37
+ if (isInvalid || validationState === "invalid") {
38
+ return true;
39
+ }
40
+
41
+ if (isInvalid === false || validationState === "valid") {
42
+ return false;
43
+ }
44
+
45
+ return undefined;
46
+ };
47
+
25
48
  export interface DatePickerProps
26
- extends Omit<
27
- TextFieldProps,
28
- "defaultValue" | "onChange" | "onBlur" | "onFocus"
29
- > {
49
+ extends Validation,
50
+ Omit<TextFieldProps, "defaultValue" | "onChange" | "onBlur" | "onFocus"> {
30
51
  onChange?: (date: string | undefined) => void;
31
52
  defaultValue?: string;
32
53
  min?: string;
@@ -40,6 +61,7 @@ export const DatePicker = (props: DatePickerProps) => {
40
61
  defaultValue = "",
41
62
  isDisabled,
42
63
  validationState,
64
+ isInvalid,
43
65
  errorMessage = "",
44
66
  ...otherProps
45
67
  } = props;
@@ -50,28 +72,35 @@ export const DatePicker = (props: DatePickerProps) => {
50
72
  const [textFieldVal, setTextFieldVal] = useState<string>(
51
73
  validateDateFormat(defaultValue),
52
74
  );
53
- const [isValid, setIsValid] = useState<boolean>(true);
54
- const errorMessageText = !isValid
75
+ const [isValid, setIsValid] = useState<boolean | undefined>(undefined);
76
+ const isInvalidProp = getValidationState({
77
+ validationState,
78
+ isInvalid: isValid === false || isInvalid,
79
+ });
80
+ const errorMessageText = isInvalidProp
55
81
  ? // eslint-disable-next-line react/destructuring-assignment
56
82
  formatErrorMessageText(textFieldVal, props.min, props.max)
57
83
  : errorMessage;
58
- const inputValidationState = !isValid ? "invalid" : validationState;
59
84
  const touchDevice = isTouchDevice();
60
85
 
61
- const validationClasses = {
62
- "--is-valid": validationState === "valid",
63
- "--is-invalid": validationState === "invalid" || !isValid,
64
- };
65
-
66
- const containerClasses = classNames("mobius/DatePickerContainer", {
67
- "--is-disabled": isDisabled,
68
- "--is-touch-device": touchDevice,
69
- ...validationClasses,
86
+ const validationClasses = useValidationClasses({
87
+ validationState,
88
+ isInvalid: isInvalidProp,
70
89
  });
71
90
 
72
- const popoverToggleClasses = classNames("mobius/DateFieldButton", {
73
- ...validationClasses,
74
- });
91
+ const containerClasses = classNames(
92
+ "mobius/DatePickerContainer",
93
+ {
94
+ "--is-disabled": isDisabled,
95
+ "--is-touch-device": touchDevice,
96
+ },
97
+ validationClasses,
98
+ );
99
+
100
+ const popoverToggleClasses = classNames(
101
+ "mobius/DateFieldButton",
102
+ validationClasses,
103
+ );
75
104
 
76
105
  const togglePopoverVisibility = () => {
77
106
  setIsValid(true);
@@ -143,7 +172,7 @@ export const DatePicker = (props: DatePickerProps) => {
143
172
  onChange={handleTextFieldChange}
144
173
  value={textFieldVal}
145
174
  isDisabled={isDisabled}
146
- validationState={inputValidationState}
175
+ isInvalid={isInvalidProp}
147
176
  {...otherProps}
148
177
  errorMessage={errorMessageText}
149
178
  />
@@ -162,7 +191,7 @@ export const DatePicker = (props: DatePickerProps) => {
162
191
  onChange={handleTextFieldChange}
163
192
  value={textFieldVal}
164
193
  isDisabled={isDisabled}
165
- validationState={inputValidationState}
194
+ isInvalid={isInvalidProp}
166
195
  {...otherProps}
167
196
  errorMessage={errorMessageText}
168
197
  suffixOutside={
@@ -171,6 +200,7 @@ export const DatePicker = (props: DatePickerProps) => {
171
200
  onClick={togglePopoverVisibility}
172
201
  isDisabled={isDisabled}
173
202
  >
203
+ <Icon size="sm" icon={calendarDay} />
174
204
  <VisuallyHidden>Pick date</VisuallyHidden>
175
205
  </Button>
176
206
  }