From 9c74b3d103f825f749a5b96d2d69358ba2d24fb8 Mon Sep 17 00:00:00 2001 From: Rushi Vishavadia Date: Mon, 4 Mar 2024 17:26:50 +0530 Subject: [PATCH 01/14] X2-9090 Ability to customize `Breakdown` component, and fix line break for long strings (#309) * X2-9090 Add tailwind-merge module and `cn` * Add clsx for Breakdown components and revert tailwind-merge `tailwind-merge` was reverted because Storybook uses Webpack 4 and doesn't support optional chaining properly * Tweak prettier arg --------- Co-authored-by: Oleksandr Khomyakov --- package-lock.json | 34 +++++++++++++++------ package.json | 2 +- src/components/Breakdown.jsx | 42 +++++++++++++++++++------- src/stories/Other/Breakdown.stories.js | 4 +++ 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 140ee8983..6e48185f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1766,16 +1766,21 @@ } }, "node_modules/@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -25292,7 +25297,8 @@ }, "node_modules/regenerator-runtime": { "version": "0.13.9", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true }, "node_modules/regenerator-transform": { "version": "0.14.5", @@ -32280,11 +32286,18 @@ } }, "@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + } } }, "@babel/template": { @@ -49708,7 +49721,8 @@ }, "regenerator-runtime": { "version": "0.13.9", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true }, "regenerator-transform": { "version": "0.14.5", diff --git a/package.json b/package.json index df1f12dbd..085626628 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "lint": "xola-lint src --ignore src/stories", "lint:fix": "xola-lint src --fix --ignore src/stories", "lint:report": "xola-lint src --ignore src/stories --reporter=json src > eslint_report.json", - "format": "prettier --write src", + "format": "prettier -l --write src", "test": "jest", "chromatic": "chromatic --exit-zero-on-changes --build-script-name build:storybook" }, diff --git a/src/components/Breakdown.jsx b/src/components/Breakdown.jsx index e1ad43085..dd476c32e 100644 --- a/src/components/Breakdown.jsx +++ b/src/components/Breakdown.jsx @@ -1,7 +1,7 @@ -import clsx from "clsx"; import PropTypes from "prop-types"; import React, { createContext, useContext, useMemo } from "react"; import { isNumber } from "lodash"; +import clsx from "clsx"; import { Currency } from "./Utilities/Currency"; const colors = { @@ -35,16 +35,26 @@ Breakdown.propTypes = { locale: PropTypes.string, }; -const BreakdownItem = ({ children, info, methodIcon, secondary, value, className, color = "default", ...rest }) => { - /** When BreakdownItem is directly used without outer component, the context would be `undefined` */ +const BreakdownItem = ({ + info, + methodIcon, + secondary, + value, + color = "default", + className, + classNames = { key: "", children: "", info: "", value: "" }, + children, + ...rest +}) => { + // When BreakdownItem is directly used without outer component, the context would be `undefined` const { currency, locale } = useContext(CurrencyContext) ?? {}; return ( - + {methodIcon} - {children} - + {children} + {info && ( {info} )} @@ -52,7 +62,7 @@ const BreakdownItem = ({ children, info, methodIcon, secondary, value, className - + {isNumber(value) ? ( {value} @@ -72,21 +82,30 @@ BreakdownItem.propTypes = { secondary: PropTypes.node, value: PropTypes.node, className: PropTypes.string, + classNames: PropTypes.object, color: PropTypes.oneOf(Object.keys(colors)), }; Breakdown.Item = BreakdownItem; Breakdown.Item.displayName = "Breakdown.Item"; -const BreakdownSubtotalItem = ({ children, info, value, className, color = "black", ...rest }) => { +const BreakdownSubtotalItem = ({ + info, + value, + color = "black", + className, + classNames = { children: "", info: "", value: "" }, + children, + ...rest +}) => { const { currency, locale } = useContext(CurrencyContext) ?? {}; return ( - {children} - {info} + {children} + {info} - + {value} @@ -100,6 +119,7 @@ BreakdownSubtotalItem.propTypes = { info: PropTypes.node, value: PropTypes.node, className: PropTypes.string, + classNames: PropTypes.object, color: PropTypes.oneOf(Object.keys(colors)), }; diff --git a/src/stories/Other/Breakdown.stories.js b/src/stories/Other/Breakdown.stories.js index 092c87677..c1c8fbcf5 100644 --- a/src/stories/Other/Breakdown.stories.js +++ b/src/stories/Other/Breakdown.stories.js @@ -54,6 +54,10 @@ export const Default = () => { This is a really long message that should wrap somehow + } value={0} methodIcon={}> + LongMessageThat ShouldOnlyBreakAt AWhitespaceLoremIpsum + + From 90b65d4fc6638e6eafd67284e1a88c25b0ef1685 Mon Sep 17 00:00:00 2001 From: Tanushree Chakravarty Date: Mon, 4 Mar 2024 17:28:38 +0530 Subject: [PATCH 02/14] 2.2.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6e48185f7..d024e63f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xola/ui-kit", - "version": "2.2.3", + "version": "2.2.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@xola/ui-kit", - "version": "2.2.3", + "version": "2.2.4", "license": "MIT", "dependencies": { "@headlessui/react": "^1.4.0", diff --git a/package.json b/package.json index 085626628..8371b09e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xola/ui-kit", - "version": "2.2.3", + "version": "2.2.4", "description": "Xola UI Kit", "license": "MIT", "repository": { From fbbd0e9ebf92eca34225d18920c1ee2d4a37f561 Mon Sep 17 00:00:00 2001 From: kirtesh-xola <136308620+kirtesh-xola@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:19:36 +0530 Subject: [PATCH 03/14] feat(icons): updates BoxIcon (#313) * XS-25 feat(purchase-guests): adds `CircleCheckFilledIcon` and `WarningFilledIcon` * feat(icons): updates `BoxIcon` --- src/icons/src/BoxIcon.jsx | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/icons/src/BoxIcon.jsx b/src/icons/src/BoxIcon.jsx index fb5e144c5..0beb1c4f6 100644 --- a/src/icons/src/BoxIcon.jsx +++ b/src/icons/src/BoxIcon.jsx @@ -3,19 +3,25 @@ import { createIcon } from "./helpers/icon"; export const BoxIcon = createIcon((props) => { return ( - - - + + + + + + + + + + + + ); }); From c90b894230da295e37eadfa2683b4c5db41a4f0a Mon Sep 17 00:00:00 2001 From: Manoj Vaibhav Date: Thu, 7 Mar 2024 15:53:54 +0530 Subject: [PATCH 04/14] 2.2.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d024e63f5..3315631bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xola/ui-kit", - "version": "2.2.4", + "version": "2.2.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@xola/ui-kit", - "version": "2.2.4", + "version": "2.2.5", "license": "MIT", "dependencies": { "@headlessui/react": "^1.4.0", diff --git a/package.json b/package.json index 8371b09e1..e86407b56 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xola/ui-kit", - "version": "2.2.4", + "version": "2.2.5", "description": "Xola UI Kit", "license": "MIT", "repository": { From b508ef1eeec0b6fdb9e229b01017fd37776e9562 Mon Sep 17 00:00:00 2001 From: sachin patil Date: Thu, 14 Mar 2024 16:33:15 +0530 Subject: [PATCH 05/14] X2-9236: Added rangename to value in datepicker (#315) --- src/components/DatePicker/DatePicker.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DatePicker/DatePicker.jsx b/src/components/DatePicker/DatePicker.jsx index 01f47607d..44ff4869b 100644 --- a/src/components/DatePicker/DatePicker.jsx +++ b/src/components/DatePicker/DatePicker.jsx @@ -97,7 +97,7 @@ export const DatePicker = ({ const handleRelativeRangeChanged = (rangeName, range) => { setCurrentMonth(range.from); setStartMonth(range.from); - onChange(range, modifiers, null); + onChange({ ...range, rangeName }, modifiers, null); }; const handleMonthChange = (m) => { @@ -237,7 +237,7 @@ export const DatePicker = ({ {components.Footer ? : null} {useDateRangeStyle && shouldShowRelativeRanges && ( -
+
Date: Thu, 14 Mar 2024 16:47:13 +0530 Subject: [PATCH 06/14] 2.2.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3315631bd..2ce9a1d51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xola/ui-kit", - "version": "2.2.5", + "version": "2.2.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@xola/ui-kit", - "version": "2.2.5", + "version": "2.2.6", "license": "MIT", "dependencies": { "@headlessui/react": "^1.4.0", diff --git a/package.json b/package.json index e86407b56..83b43bbb0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xola/ui-kit", - "version": "2.2.5", + "version": "2.2.6", "description": "Xola UI Kit", "license": "MIT", "repository": { From 1df8d4090e00692aafee0359eef7be0075b3eb34 Mon Sep 17 00:00:00 2001 From: kirtesh-xola <136308620+kirtesh-xola@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:10:38 +0530 Subject: [PATCH 07/14] fix(input): adds support for prefix and suffix while showing isRequired indicator (#314) * XS-25 feat(purchase-guests): adds `CircleCheckFilledIcon` and `WarningFilledIcon` * fix(input): adds support for `prefix` and `suffix` while showing isRequired indicator * chore(input): adds reason for prefix/suffix removal * chore(input): resolves [react-hooks/exhaustive-deps] lint warning * Update src/components/Forms/BaseInput.jsx Co-authored-by: Rushi Vishavadia * Update src/components/Forms/BaseInput.jsx Co-authored-by: Rushi Vishavadia --------- Co-authored-by: Manoj Vaibhav Co-authored-by: Rushi Vishavadia --- src/components/Forms/BaseInput.jsx | 42 +++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/components/Forms/BaseInput.jsx b/src/components/Forms/BaseInput.jsx index 3f88c34f9..7e5bf8e96 100644 --- a/src/components/Forms/BaseInput.jsx +++ b/src/components/Forms/BaseInput.jsx @@ -1,6 +1,6 @@ import clsx from "clsx"; import PropTypes from "prop-types"; -import React, { forwardRef } from "react"; +import React, { forwardRef, useMemo } from "react"; import { isEmpty, isString } from "lodash"; import { Dot } from "../Dot/Dot"; @@ -11,9 +11,39 @@ const sizes = { }; export const BaseInput = forwardRef( - ({ as: Tag, size = "medium", isError, className, isRequired, value, ...rest }, ref) => { - // added regexp to return only non-whitespace characters for strings and remove currency sign for currency input - const stringValue = isString(value) && value.replace(/[^.\S]+/g, "").replace(/[\p{Sc}]/u, ""); + ({ as: Tag, size = "medium", isError, className, isRequired, value, prefix, suffix, ...rest }, ref) => { + const stringValue = useMemo(() => { + if (!isString(value)) return undefined; + + let result = value; + + /** + * Why are we removing prefix and suffix? + * + * When BaseInput field is used as a component for `NumberFormat` input, the value would also include prefix and suffix. + * + * For Example, in UnitField (Weight) we have a suffix as " kg". + * So, if a user enters 72, the `value` prop supplied to this component would be "72 kg". + * and if the field is empty, the `value` would be " kg". + * + * Now, if we have `isRequired` set as true, and we need to show the required indicator when the field is empty. + * We need a way to know if the `value` supplied to this component is really a value or just prefix or suffix text. + * So, we are removing the prefix/suffix from value before comparing. + */ + + // if input contains prefix or suffix, we need to remove them + if (prefix) result = result.replace(new RegExp(`^${prefix}`), ""); + + if (suffix) result = result.replace(new RegExp(`${suffix}$`), ""); + + // Remove whitespace characters currency sign for currency input + return result. + // Remove one or more whitespace characters that are not followed by a period + replace(/[^.\S]+/g, ""). + // Remove any currency symbold + replace(/[\p{Sc}]/u, ""); + }, [value, prefix, suffix]); + // Since the input can only be a string or a number, added the toString method for a numeric value, because lodash's IsEmpty method returns true for any number. const isEmptyValue = isString(value) ? isEmpty(stringValue) : isEmpty(value?.toString()); @@ -47,6 +77,8 @@ BaseInput.propTypes = { isRequired: PropTypes.bool, // eslint-disable-next-line react/require-default-props value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + prefix: PropTypes.string, + suffix: PropTypes.string, }; BaseInput.defaultProps = { @@ -55,4 +87,6 @@ BaseInput.defaultProps = { className: "", isError: false, isRequired: false, + prefix: "", + suffix: "", }; From e5bb167e4b45ec7d821a3321698820be9fb8efd9 Mon Sep 17 00:00:00 2001 From: Manoj Vaibhav Date: Wed, 20 Mar 2024 15:12:04 +0530 Subject: [PATCH 08/14] 2.2.7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ce9a1d51..f6b1d77d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xola/ui-kit", - "version": "2.2.6", + "version": "2.2.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@xola/ui-kit", - "version": "2.2.6", + "version": "2.2.7", "license": "MIT", "dependencies": { "@headlessui/react": "^1.4.0", diff --git a/package.json b/package.json index 83b43bbb0..f1561d4f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xola/ui-kit", - "version": "2.2.6", + "version": "2.2.7", "description": "Xola UI Kit", "license": "MIT", "repository": { From c770339301e8a6fa6996f9f9bf1a5eea9b6f3691 Mon Sep 17 00:00:00 2001 From: Manoj Vaibhav Date: Wed, 20 Mar 2024 19:55:32 +0530 Subject: [PATCH 09/14] Revert "fix(input): adds support for prefix and suffix while showing isRequired indicator" (#317) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "fix(input): adds support for prefix and suffix while showing isRequir…" This reverts commit 1df8d4090e00692aafee0359eef7be0075b3eb34. --- src/components/Forms/BaseInput.jsx | 42 +++--------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/src/components/Forms/BaseInput.jsx b/src/components/Forms/BaseInput.jsx index 7e5bf8e96..3f88c34f9 100644 --- a/src/components/Forms/BaseInput.jsx +++ b/src/components/Forms/BaseInput.jsx @@ -1,6 +1,6 @@ import clsx from "clsx"; import PropTypes from "prop-types"; -import React, { forwardRef, useMemo } from "react"; +import React, { forwardRef } from "react"; import { isEmpty, isString } from "lodash"; import { Dot } from "../Dot/Dot"; @@ -11,39 +11,9 @@ const sizes = { }; export const BaseInput = forwardRef( - ({ as: Tag, size = "medium", isError, className, isRequired, value, prefix, suffix, ...rest }, ref) => { - const stringValue = useMemo(() => { - if (!isString(value)) return undefined; - - let result = value; - - /** - * Why are we removing prefix and suffix? - * - * When BaseInput field is used as a component for `NumberFormat` input, the value would also include prefix and suffix. - * - * For Example, in UnitField (Weight) we have a suffix as " kg". - * So, if a user enters 72, the `value` prop supplied to this component would be "72 kg". - * and if the field is empty, the `value` would be " kg". - * - * Now, if we have `isRequired` set as true, and we need to show the required indicator when the field is empty. - * We need a way to know if the `value` supplied to this component is really a value or just prefix or suffix text. - * So, we are removing the prefix/suffix from value before comparing. - */ - - // if input contains prefix or suffix, we need to remove them - if (prefix) result = result.replace(new RegExp(`^${prefix}`), ""); - - if (suffix) result = result.replace(new RegExp(`${suffix}$`), ""); - - // Remove whitespace characters currency sign for currency input - return result. - // Remove one or more whitespace characters that are not followed by a period - replace(/[^.\S]+/g, ""). - // Remove any currency symbold - replace(/[\p{Sc}]/u, ""); - }, [value, prefix, suffix]); - + ({ as: Tag, size = "medium", isError, className, isRequired, value, ...rest }, ref) => { + // added regexp to return only non-whitespace characters for strings and remove currency sign for currency input + const stringValue = isString(value) && value.replace(/[^.\S]+/g, "").replace(/[\p{Sc}]/u, ""); // Since the input can only be a string or a number, added the toString method for a numeric value, because lodash's IsEmpty method returns true for any number. const isEmptyValue = isString(value) ? isEmpty(stringValue) : isEmpty(value?.toString()); @@ -77,8 +47,6 @@ BaseInput.propTypes = { isRequired: PropTypes.bool, // eslint-disable-next-line react/require-default-props value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - prefix: PropTypes.string, - suffix: PropTypes.string, }; BaseInput.defaultProps = { @@ -87,6 +55,4 @@ BaseInput.defaultProps = { className: "", isError: false, isRequired: false, - prefix: "", - suffix: "", }; From f70f15b2a1d14af847ffc1c8b14242a531069b42 Mon Sep 17 00:00:00 2001 From: Manoj Vaibhav Date: Wed, 20 Mar 2024 19:56:56 +0530 Subject: [PATCH 10/14] 2.2.8 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f6b1d77d0..129c92e05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xola/ui-kit", - "version": "2.2.7", + "version": "2.2.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@xola/ui-kit", - "version": "2.2.7", + "version": "2.2.8", "license": "MIT", "dependencies": { "@headlessui/react": "^1.4.0", diff --git a/package.json b/package.json index f1561d4f8..b65a54043 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xola/ui-kit", - "version": "2.2.7", + "version": "2.2.8", "description": "Xola UI Kit", "license": "MIT", "repository": { From 39dcf84d4e68d264cb4055b9c6862d809583fd4b Mon Sep 17 00:00:00 2001 From: sachin patil Date: Wed, 27 Mar 2024 18:39:40 +0530 Subject: [PATCH 11/14] X2-9301, X2-9204: Fixed timezone for dates in DatePicker (#316) * X2-9301: Fixed timezone for dates in DatePicker * added package-lock.json back * lint fixes * X2-9301: Fixed timezone for dates in DatePicker * temp commit checking some logic * Fix relative date range picker display * Fix styling when start and end is same * exception hadling * code review feedback * bug fix, isSame function date is not dayjs date * fix(date picker) Update the end month when you change date range * fix(date picker) Fix typo with selecting last quarter * X2-9301: removed default timezone set in datepicker * X2-9301: removed default timezone set in datepicker * bug fix last quarter * bug fix last quarter --------- Co-authored-by: Rushi Vishavadia --- src/components/DatePicker/DatePicker.css | 15 ++- src/components/DatePicker/DatePicker.jsx | 63 +++++++---- src/components/DatePicker/RangeDatePicker.jsx | 12 +- .../DatePicker/RelativeDateRange.jsx | 106 +++++++++--------- src/helpers/date.js | 77 ++++++++++++- .../DataDisplay/DateRangePicker.stories.js | 25 ++++- 6 files changed, 212 insertions(+), 86 deletions(-) diff --git a/src/components/DatePicker/DatePicker.css b/src/components/DatePicker/DatePicker.css index 1b6f3a626..39053460f 100644 --- a/src/components/DatePicker/DatePicker.css +++ b/src/components/DatePicker/DatePicker.css @@ -167,8 +167,8 @@ @apply !bg-blue !text-white; } -/* Make the start date have a 50% light blue background towards the RIGHT side */ -.date-range-picker .DayPicker-Day--start:not(.DayPicker-Day--outside) { +/* Make the start date have a 50% light blue background towards the RIGHT side when start and end is not same */ +.date-range-picker .DayPicker-Day--start:not(.DayPicker-Day--outside):not(.DayPicker-Day--end) { @apply rounded-none; background: linear-gradient(90deg, #ffffff 40%, #d1e1ff 25%); } @@ -177,18 +177,23 @@ @apply rounded-full; } -/* Make the end date have a 50% light blue background towards the LEFT side */ -.date-range-picker .DayPicker-Day--end:not(.DayPicker-Day--outside) { +/* Make the end date have a 50% light blue background towards the LEFT side when start and end is not same */ +.date-range-picker .DayPicker-Day--end:not(.DayPicker-Day--outside):not(.DayPicker-Day--start) { @apply rounded-none; background: linear-gradient(90deg, #d1e1ff 40%, #ffffff 25%); /* D1E1FF Blue lighter */ } +/* This is when start & end are the same. We shown an outline to indicate it is different */ +.date-range-picker .DayPicker-Day--start.DayPicker-Day--end:not(.DayPicker-Day--disabled) .ui-date-picker-day { + @apply outline outline-offset-1 outline-blue-lighter; +} + .date-range-picker .DayPicker-Day--end div { @apply rounded-full; } /** - * These are stylings for the Date Picker, but only when they have custom content like prices or seller messaging + * These are styles for the Date Picker, but only when they have custom content like prices or seller messaging */ .has-custom-content .DayPicker-Month { border-spacing: 1px 16px; diff --git a/src/components/DatePicker/DatePicker.jsx b/src/components/DatePicker/DatePicker.jsx index 44ff4869b..a2a21306f 100644 --- a/src/components/DatePicker/DatePicker.jsx +++ b/src/components/DatePicker/DatePicker.jsx @@ -7,6 +7,7 @@ import "react-day-picker/lib/style.css"; import "./DatePicker.css"; import { isArray, isFunction } from "lodash"; import { Tooltip } from "../.."; +import { now, isSame, toDate, isValidTimeZoneName } from "../../helpers/date"; import { Day } from "./Day"; import { MonthYearSelector } from "./MonthYearSelector"; import { RelativeDateRange } from "./RelativeDateRange"; @@ -41,7 +42,7 @@ export const DatePicker = ({ ...rest }) => { const initialValue = value ? (variant === variants.single ? value : value.from) : null; - const [currentMonth, setCurrentMonth] = useState(initialValue ?? dayjs().toDate()); + const [currentMonth, setCurrentMonth] = useState(initialValue ?? now(null, timezoneName).toDate()); const [startMonth, setStartMonth] = useState(() => { if (!value || !value.from) { return new Date(); @@ -51,11 +52,11 @@ export const DatePicker = ({ }); const [endMonth, setEndMonth] = useState(() => { if (!value || !value.to || !value.from) { - return dayjs(new Date()).add(1, "month").toDate(); + return now(null, timezoneName).add(1, "month").toDate(); } - return dayjs(value.to).isSame(dayjs(value.from), "month") - ? dayjs(value.from).add(1, "month").toDate() + return isSame(now(value.to, timezoneName), now(value.from, timezoneName), "month") + ? now(value.from, timezoneName).add(1, "month").toDate() : value.to; }); const [rangeName, setRangeName] = useState(""); @@ -67,24 +68,31 @@ export const DatePicker = ({ onMonthChange?.(currentMonth); }, [currentMonth, onMonthChange]); + useEffect(() => { + if (timezoneName && !isValidTimeZoneName(timezoneName)) { + console.log(`${timezoneName} is not a valid timezone. Using default timezone now`); + dayjs.tz.setDefault(); + } + }, [timezoneName]); + const handleTodayClick = (day, options, event) => { if (isRangeVariant) { return; } - const today = timezoneName ? dayjs().tz(timezoneName).toDate() : new Date(); + const today = timezoneName ? toDate(now(day, timezoneName)) : new Date(); if (options.disabled || isDisabled(today)) { setCurrentMonth(today); onMonthChange?.(today); } else { - onChange(day, options, event); + onChange(today, options, event); } }; const isDisabled = (date) => { if (isArray(disabledDays)) { - return disabledDays.some((_date) => dayjs(_date).isSame(date, "day")); + return disabledDays.some((_date) => isSame(now(_date, timezoneName), date, "day")); } if (isFunction(disabledDays)) { @@ -97,6 +105,7 @@ export const DatePicker = ({ const handleRelativeRangeChanged = (rangeName, range) => { setCurrentMonth(range.from); setStartMonth(range.from); + setEndMonth(range.to); onChange({ ...range, rangeName }, modifiers, null); }; @@ -120,7 +129,7 @@ export const DatePicker = ({ return; } - if (dayjs(value?.from).isSame(day, "month")) { + if (isSame(now(value?.from, timezoneName), now(day, timezoneName), "month")) { handleStartMonthChange(day); } @@ -129,25 +138,31 @@ export const DatePicker = ({ if (isValidValue) { // This allows us to easily select another date range, // if both dates are selected. - onChange({ from: day, to: null }, options, event); + onChange({ from: toDate(now(day, timezoneName).startOf("day")), to: null }, options, event); } else if (value && (value.from || value.to) && (value.from || value.to).getTime() === day.getTime()) { - const from = dayjs(day).startOf("day").toDate(); - const to = dayjs(day).endOf("day").toDate(); + const from = toDate(now(day, timezoneName).startOf("day")); + const to = toDate(now(day, timezoneName).endOf("day"), false); onChange({ from, to }, options, event); } else { - onChange(DateUtils.addDayToRange(day, value), options, event); + onChange( + DateUtils.addDayToRange(toDate(now(day, timezoneName).endOf("day"), false), value), + options, + event, + ); } } else { - onChange(day, options, event); + onChange(toDate(now(day, timezoneName)), options, event); } }; + // TODO: Should be outside this component because this returns JSX const CaptionElement = shouldShowYearPicker && currentMonth ? ({ date }) => : undefined; + // TODO: Should be outside this component because this returns JSX const renderDay = (date) => { const tooltipContent = getTooltip?.(date); const disabled = isDisabled(date); @@ -178,7 +193,7 @@ export const DatePicker = ({ // Comparing `from` and `to` dates hides a weird CSS style when you select the same date twice in a date range. const useDateRangeStyle = isRangeVariant && isValidValue && value.from?.getTime() !== value.to?.getTime(); // Return the same value if it is already dayjs object or has range variant otherwise format it to dayJs object - const selectedDays = value && (dayjs.isDayjs(value) || isRangeVariant ? value : dayjs(value).toDate()); + const selectedDays = value && (dayjs.isDayjs(value) || isRangeVariant ? value : now(value, timezoneName).toDate()); return ( <> @@ -208,6 +223,7 @@ export const DatePicker = ({ handleEndMonthChange={handleEndMonthChange} handleTodayClick={handleTodayClick} selectedDays={selectedDays} + timezoneName={timezoneName} {...rest} /> ) : ( @@ -236,14 +252,17 @@ export const DatePicker = ({ {components.Footer ? : null} - {useDateRangeStyle && shouldShowRelativeRanges && ( -
- + {shouldShowRelativeRanges && ( +
+
+ +
)} diff --git a/src/components/DatePicker/RangeDatePicker.jsx b/src/components/DatePicker/RangeDatePicker.jsx index 75a1d846f..06d83994f 100644 --- a/src/components/DatePicker/RangeDatePicker.jsx +++ b/src/components/DatePicker/RangeDatePicker.jsx @@ -2,9 +2,9 @@ import React from "react"; import PropTypes from "prop-types"; import DayPicker from "react-day-picker"; import clsx from "clsx"; -import dayjs from "dayjs"; import { isArray, isFunction } from "lodash"; import { Tooltip } from "../Tooltip"; +import { now } from "../../helpers/date"; import { NavbarElement } from "./NavbarElement"; import { MonthYearSelector } from "./MonthYearSelector"; import { Day } from "./Day"; @@ -24,10 +24,11 @@ const RangeDatePicker = ({ handleStartMonthChange, handleEndMonthChange, handleTodayClick, + timezoneName, ...rest }) => { - const isStartDateIsTheSameMonth = dayjs(value?.from).isSame(dayjs(value?.to), "month"); - const isSingleDayDateRange = dayjs(value?.from).isSame(dayjs(value.to), "day"); + const isStartDateIsTheSameMonth = now(value?.from, timezoneName).isSame(now(value?.to, timezoneName), "month"); + const isSingleDayDateRange = now(value?.from, timezoneName).isSame(now(value?.to, timezoneName), "day"); const createCaptionElement = (currentMonth, handleChange) => shouldShowYearPicker && currentMonth @@ -43,7 +44,7 @@ const RangeDatePicker = ({ } if (isArray(disabledDays)) { - return disabledDays.some((_date) => dayjs(_date).isSame(date, "day")); + return disabledDays.some((_date) => now(_date, timezoneName).isSame(date, "day")); } return false; @@ -54,7 +55,7 @@ const RangeDatePicker = ({ }; const isDisabledEndDays = (date) => { - const isDateBeforeStartDate = dayjs(date).isBefore(value?.from, "day"); + const isDateBeforeStartDate = now(date, timezoneName).isBefore(value?.from, "day"); return isDateDisabledFromOutside(date) || (isDateBeforeStartDate && !isSingleDayDateRange); }; @@ -148,6 +149,7 @@ RangeDatePicker.propTypes = { handleStartMonthChange: PropTypes.func, handleEndMonthChange: PropTypes.func, handleTodayClick: PropTypes.func, + timezoneName: PropTypes.string, }; export default RangeDatePicker; diff --git a/src/components/DatePicker/RelativeDateRange.jsx b/src/components/DatePicker/RelativeDateRange.jsx index 1fa660696..6e0d83e7a 100644 --- a/src/components/DatePicker/RelativeDateRange.jsx +++ b/src/components/DatePicker/RelativeDateRange.jsx @@ -1,6 +1,6 @@ -import dayjs from "dayjs"; import PropTypes from "prop-types"; import React from "react"; +import { now, toDate } from "../../helpers/date"; import { Button, Select } from "../.."; const options = { @@ -113,98 +113,102 @@ export const dateRanges = { }; const handlers = { - [options.YESTERDAY]: () => { - const yesterday = dayjs().subtract(1, "day"); + [options.YESTERDAY]: (timezone) => { + const yesterday = now(null, timezone).subtract(1, "day"); return { - from: yesterday.startOf("day").toDate(), - to: yesterday.endOf("day").toDate(), + from: toDate(yesterday.startOf("day")), + to: toDate(yesterday.endOf("day"), false), }; }, - [options.TODAY]: () => ({ - from: dayjs().startOf("day").toDate(), - to: dayjs().endOf("day").toDate(), - }), + [options.TODAY]: (timezone) => { + return { + from: toDate(now(null, timezone).startOf("day")), + to: toDate(now(null, timezone).endOf("day"), false), + }; + }, - [options.LAST_WEEK]: () => { - const lastWeek = dayjs().subtract(7, "day"); + [options.LAST_WEEK]: (timezone) => { + const lastWeek = now(null, timezone).subtract(7, "day"); return { - from: lastWeek.startOf("week").toDate(), - to: lastWeek.endOf("week").toDate(), + from: toDate(lastWeek.startOf("week")), + to: toDate(lastWeek.endOf("week"), false), }; }, - [options.TRAILING_WEEK]: () => { + [options.TRAILING_WEEK]: (timezone) => { return { - from: dayjs().subtract(7, "day").startOf("dat").toDate(), - to: dayjs().subtract(1, "day").endOf("day").toDate(), + from: toDate(now(null, timezone).subtract(7, "day").startOf("day")), + to: toDate(now(null, timezone).subtract(1, "day").endOf("day"), false), }; }, - [options.THIS_WEEK]: () => ({ - from: dayjs().startOf("week").toDate(), - to: dayjs().endOf("week").toDate(), - }), + [options.THIS_WEEK]: (timezone) => { + return { + from: toDate(now(null, timezone).startOf("week")), + to: toDate(now(null, timezone).endOf("week"), false), + }; + }, - [options.LAST_MONTH]: () => { - const lastMonth = dayjs().subtract(1, "month"); + [options.LAST_MONTH]: (timezone) => { + const lastMonth = now(null, timezone).subtract(1, "month"); return { - from: lastMonth.startOf("month").toDate(), - to: lastMonth.endOf("month").toDate(), + from: toDate(lastMonth.startOf("month")), + to: toDate(lastMonth.endOf("month"), false), }; }, - [options.TRAILING_MONTH]: () => { + [options.TRAILING_MONTH]: (timezone) => { return { - from: dayjs().subtract(1, "month").startOf("day").toDate(), - to: dayjs().subtract(1, "day").endOf("day").toDate(), + from: toDate(now(null, timezone).subtract(1, "month").startOf("day")), + to: toDate(now(null, timezone).subtract(1, "day").endOf("day"), false), }; }, - [options.THIS_MONTH]: () => ({ - from: dayjs().startOf("month").toDate(), - to: dayjs().endOf("month").toDate(), + [options.THIS_MONTH]: (timezone) => ({ + from: toDate(now(null, timezone).startOf("month")), + to: toDate(now(null, timezone).endOf("month"), false), }), - [options.LAST_QUARTER]: () => { + [options.LAST_QUARTER]: (timezone) => { return { - from: dayjs().startOf("month").subtract(3, "month").toDate(), - to: dayjs().startOf("month").subtract(1, "day").toDate(), + from: toDate(now(null, timezone).startOf("quarter").subtract(3, "month").startOf("month")), + to: toDate(now(null, timezone).endOf("quarter").subtract(3, "month").endOf("month"), false), }; }, - [options.TRAILING_QUARTER]: () => { + [options.TRAILING_QUARTER]: (timezone) => { return { - from: dayjs().subtract(3, "month").startOf("day").toDate(), - to: dayjs().subtract(1, "day").endOf("day").toDate(), + from: toDate(now(null, timezone).subtract(3, "month").startOf("day")), + to: toDate(now(null, timezone).subtract(1, "day").endOf("day"), false), }; }, - [options.THIS_QUARTER]: () => { + [options.THIS_QUARTER]: (timezone) => { return { - from: dayjs().startOf("Q").toDate(), - to: dayjs().endOf("Q").toDate(), + from: toDate(now(null, timezone).startOf("Q")), + to: toDate(now(null, timezone).endOf("Q"), false), }; }, - [options.LAST_YEAR]: () => { - const lastYear = dayjs().subtract(1, "year"); + [options.LAST_YEAR]: (timezone) => { + const lastYear = now(null, timezone).subtract(1, "year"); return { - from: lastYear.startOf("year").toDate(), - to: lastYear.endOf("year").toDate(), + from: toDate(lastYear.startOf("year")), + to: toDate(lastYear.endOf("year"), false), }; }, - [options.TRAILING_YEAR]: () => { + [options.TRAILING_YEAR]: (timezone) => { return { - from: dayjs().subtract(1, "year").startOf("day").toDate(), - to: dayjs().subtract(1, "day").endOf("day").toDate(), + from: toDate(now(null, timezone).subtract(1, "year").startOf("day")), + to: toDate(now(null, timezone).subtract(1, "day").endOf("day"), false), }; }, - [options.THIS_YEAR]: () => ({ - from: dayjs().startOf("year").toDate(), - to: dayjs().endOf("year").toDate(), + [options.THIS_YEAR]: (timezone) => ({ + from: toDate(now(null, timezone).startOf("year")), + to: toDate(now(null, timezone).endOf("year"), false), }), }; @@ -215,10 +219,11 @@ export const RelativeDateRange = ({ showApply = true, onChange, onSubmit, + timezoneName, }) => { const handleChange = (e) => { const rangeName = e.target.value; - const range = handlers[rangeName](); + const range = handlers[rangeName](timezoneName); onChange(rangeName, range); }; @@ -252,4 +257,5 @@ RelativeDateRange.propTypes = { // eslint-disable-next-line react/boolean-prop-naming showApply: PropTypes.bool, value: PropTypes.string, + timezoneName: PropTypes.string, }; diff --git a/src/helpers/date.js b/src/helpers/date.js index 06cb79a0d..1cf1fe063 100644 --- a/src/helpers/date.js +++ b/src/helpers/date.js @@ -1,13 +1,31 @@ -import dayjs from "dayjs"; +import dayjs, { isDayjs } from "dayjs"; import LocalizedFormat from "dayjs/plugin/localizedFormat"; import customParseFormat from "dayjs/plugin/customParseFormat"; import quarterOfYear from "dayjs/plugin/quarterOfYear"; +import utc from "dayjs/plugin/utc"; +import timezone from "dayjs/plugin/timezone"; dayjs.extend(customParseFormat); dayjs.extend(LocalizedFormat); dayjs.extend(quarterOfYear); +dayjs.extend(utc); +dayjs.extend(timezone); -export const formatDate = (date, format = "YYYY-MM-DD") => { +export const DateFormat = { + DATE_ISO: "YYYY-MM-DD", +}; + +export const isValidTimeZoneName = (timezoneName) => { + try { + dayjs.tz(new Date(), timezoneName); + } catch { + return false; + } + + return true; +}; + +export const formatDate = (date, format = DateFormat.DATE_ISO) => { return dayjs(date).format(format); }; @@ -19,3 +37,58 @@ export const formatTime = (time, format = "h:mm a") => { export const dateFromObjectId = (id) => { return dayjs(new Date(Number.parseInt(id.slice(0, 8), 16) * 1000)); }; + +export const now = (date, timezone) => { + if (!date) { + return timezone ? dayjs().tz(timezone).startOf("day") : dayjs(); + } + + if (typeof date === "number") { + const timestamp = date <= 2_147_483_647 ? date * 1000 : date; + return timezone ? dayjs(timestamp).tz(timezone) : dayjs(timestamp); + } + + if (typeof date === "string") { + return timezone ? dayjs(date).tz(timezone) : dayjs(); + } + + if (isDayjs(date)) { + // We do this late because under some conditions this is expensive (see: X2-9122) + return date; + } + + if (date instanceof Date && !Number.isNaN(date.getTime())) { + return timezone ? dayjs.tz(dateToString(date), timezone) : dayjs(dateToString(date)); + } + + return timezone ? dayjs().tz(timezone) : dayjs(); +}; + +const padNumber = (value) => value.toString().padStart(2, "0"); + +export const dateToString = (date) => { + const dateString = `${date.getFullYear()}-${padNumber(date.getMonth() + 1)}-${padNumber(date.getDate())}`; + const timeString = `${padNumber(date.getHours())}:${padNumber(date.getMinutes())}:${padNumber(date.getSeconds())}`; + return `${dateString} ${timeString}`; +}; + +const DayTimeStart = "T00:00:00"; +const DayTimeEnd = "T23:59:59"; + +export const toDate = (date, isStartDate = true) => { + const suffix = isStartDate ? DayTimeStart : DayTimeEnd; + + if (isDayjs(date)) { + return new Date(date.format(DateFormat.DATE_ISO) + suffix); + } + + return new Date(formatDate(date) + suffix); +}; + +export const isSame = (date1, date2, unit = "day") => { + if (isDayjs(date1) && isDayjs(date2)) { + return date1.isSame(date2, unit); + } + + return false; +}; diff --git a/src/stories/DataDisplay/DateRangePicker.stories.js b/src/stories/DataDisplay/DateRangePicker.stories.js index 7017e8558..f77a1e8f3 100644 --- a/src/stories/DataDisplay/DateRangePicker.stories.js +++ b/src/stories/DataDisplay/DateRangePicker.stories.js @@ -35,7 +35,10 @@ const DateRangePickerStories = { }, }; -const today = dayjs("2022-10-10").toDate(); +const today = dayjs.tz("2022-10-10").toDate(); +const handleSubmitDateRange = (e) => { + console.log("handleSubmitDateRange", { event: e }); +} export const Default = () => { const [value, setValue] = useState({ from: new Date("2022-02-03"), to: new Date("2022-03-08") }); @@ -54,7 +57,25 @@ export const RelativeDateRanges = () => { value={value} variant="range" onChange={setValue} - onSubmitDateRange={console.log} + onSubmitDateRange={handleSubmitDateRange} + /> +
+ ); +}; + +export const RelativeDateRangesWithTimeZone = () => { + const [value, setValue] = useState({ from: new Date("2022-03-03"), to: new Date("2022-04-08") }); + + return ( +
+
); From 474ef2abc4b859ef10b723417c37bd609b6e97fa Mon Sep 17 00:00:00 2001 From: Akash Babber Date: Thu, 28 Mar 2024 16:35:05 +0530 Subject: [PATCH 12/14] XS-80: added auto size textarea (#318) * XS-80: added auto size textarea * XS-80: merge commit fixes * XS-80: lint fix * XS-80: fixed story * Format + Add one-line documentation * XS-80: fixed rows prop for textarea * XS-80: prop order change --------- Co-authored-by: Rushi Vishavadia --- package-lock.json | 87 +++++++++++---------------- package.json | 1 + src/components/Forms/Textarea.jsx | 19 +++++- src/stories/Forms/Textarea.stories.js | 17 ++++++ 4 files changed, 70 insertions(+), 54 deletions(-) diff --git a/package-lock.json b/package-lock.json index 129c92e05..4a311ca10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "react-hotkeys-hook": "^3.4.0", "react-json-view": "^1.21.3", "react-select": "^5.7.0", + "react-textarea-autosize": "^8.5.3", "storybook-addon-designs": "^6.3.1", "tippy.js": "^6.3.1" }, @@ -1766,9 +1767,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", + "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -25053,19 +25054,19 @@ } }, "node_modules/react-textarea-autosize": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz", - "integrity": "sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz", + "integrity": "sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==", "dependencies": { - "@babel/runtime": "^7.10.2", - "use-composed-ref": "^1.0.0", - "use-latest": "^1.0.0" + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" }, "engines": { "node": ">=10" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/react-transition-group": { @@ -28044,11 +28045,6 @@ "node": ">=6.10" } }, - "node_modules/ts-essentials": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-2.0.12.tgz", - "integrity": "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==" - }, "node_modules/ts-pnp": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", @@ -28778,14 +28774,11 @@ } }, "node_modules/use-composed-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.1.0.tgz", - "integrity": "sha512-my1lNHGWsSDAhhVAT4MKs6IjBUtG6ZG11uUqexPH9PptiIZDQOzaF4f5tEbJ2+7qvNbtXNBbU3SfmN+fXlWDhg==", - "dependencies": { - "ts-essentials": "^2.0.3" - }, + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/use-isomorphic-layout-effect": { @@ -28802,14 +28795,14 @@ } }, "node_modules/use-latest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz", - "integrity": "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", "dependencies": { - "use-isomorphic-layout-effect": "^1.0.0" + "use-isomorphic-layout-effect": "^1.1.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -32286,9 +32279,9 @@ } }, "@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", + "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", "requires": { "regenerator-runtime": "^0.14.0" }, @@ -49539,13 +49532,13 @@ } }, "react-textarea-autosize": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz", - "integrity": "sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz", + "integrity": "sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==", "requires": { - "@babel/runtime": "^7.10.2", - "use-composed-ref": "^1.0.0", - "use-latest": "^1.0.0" + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" } }, "react-transition-group": { @@ -51905,11 +51898,6 @@ "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", "dev": true }, - "ts-essentials": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-2.0.12.tgz", - "integrity": "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==" - }, "ts-pnp": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", @@ -52433,12 +52421,9 @@ "dev": true }, "use-composed-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.1.0.tgz", - "integrity": "sha512-my1lNHGWsSDAhhVAT4MKs6IjBUtG6ZG11uUqexPH9PptiIZDQOzaF4f5tEbJ2+7qvNbtXNBbU3SfmN+fXlWDhg==", - "requires": { - "ts-essentials": "^2.0.3" - } + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==" }, "use-isomorphic-layout-effect": { "version": "1.1.2", @@ -52446,11 +52431,11 @@ "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" }, "use-latest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz", - "integrity": "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", "requires": { - "use-isomorphic-layout-effect": "^1.0.0" + "use-isomorphic-layout-effect": "^1.1.1" } }, "util": { diff --git a/package.json b/package.json index b65a54043..38b040103 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "react-hot-toast": "^2.1.0", "react-hotkeys-hook": "^3.4.0", "react-json-view": "^1.21.3", + "react-textarea-autosize": "^8.5.3", "react-select": "^5.7.0", "storybook-addon-designs": "^6.3.1", "tippy.js": "^6.3.1" diff --git a/src/components/Forms/Textarea.jsx b/src/components/Forms/Textarea.jsx index dde0dc8e0..a029d3101 100644 --- a/src/components/Forms/Textarea.jsx +++ b/src/components/Forms/Textarea.jsx @@ -1,16 +1,27 @@ import clsx from "clsx"; import PropTypes from "prop-types"; import React, { forwardRef } from "react"; +import TextareaAutosize from "react-textarea-autosize"; import { BaseInput } from "./BaseInput"; -export const Textarea = forwardRef(({ className, value, rows = 2, ...rest }, ref) => { - return ( +export const Textarea = forwardRef(({ className, value, shouldAutoSize = false, rows = 2, ...rest }, ref) => { + return shouldAutoSize ? ( + // TextareaAutosize has a few special props like minRows and maxRows instead of rows field so using rows field to feed the minRows prop. See full list here https://github.com/Andarist/react-textarea-autosize?tab=readme-ov-file#props + ) : ( + ); @@ -19,8 +30,10 @@ export const Textarea = forwardRef(({ className, value, rows = 2, ...rest }, ref Textarea.propTypes = { ...BaseInput.propTypes, rows: PropTypes.number, + shouldAutoSize: PropTypes.bool, }; Textarea.defaultProps = { rows: 2, + shouldAutoSize: false, }; diff --git a/src/stories/Forms/Textarea.stories.js b/src/stories/Forms/Textarea.stories.js index dedd3c587..aa3dfe010 100644 --- a/src/stories/Forms/Textarea.stories.js +++ b/src/stories/Forms/Textarea.stories.js @@ -70,4 +70,21 @@ export const CustomWidth = () => { ); }; +export const AutoSize = () => { + return ( + + +