From f9dd999c6e0b1d1bf280afa9ec940f4c628c67d1 Mon Sep 17 00:00:00 2001 From: Johan Sosa Date: Wed, 29 Nov 2023 11:13:48 +0100 Subject: [PATCH] fix: Overall Proptypes, linter error, circular dependencies, etc. --- .eslintrc.json | 4 +- merged_tokens.json | 4 +- packages/components/src/ThemeProvider.js | 17 +- .../components/src/dates/Calendar/Calendar.js | 2 + .../components/src/feedback/Alert/Alert.js | 9 +- .../components/src/feedback/Loader/Loader.js | 3 +- .../feedback/ScoreFeedback/ScoreFeedback.js | 4 +- .../feedback/ScoreFronstage/ScoreFronstage.js | 10 +- .../src/form/FileUpload/FileUpload.js | 18 +- .../components/src/form/FileUpload/index.js | 4 +- .../ImagePreviewInput/ImagePreviewInput.js | 15 +- .../form/InputDescription/InputDescription.js | 8 +- .../src/form/InputDescription/index.js | 4 +- .../src/form/InputLabel/InputLabel.js | 10 +- .../src/form/InputWrapper/InputWrapper.js | 2 +- .../src/form/MultiSelect/MultiSelect.js | 44 +- .../src/form/ProSwitch/ProSwitch.js | 15 +- .../src/form/ScoreInput/ScoreInput.js | 69 +- packages/components/src/form/Select/Select.js | 13 +- .../src/form/TableInput/TableInput.js | 8 +- .../src/form/TableInput/TableInputDisplay.js | 17 +- .../src/form/TableInput/TableInputRow.js | 208 +- .../src/form/TagifyInput/TagifyInput.js | 14 +- .../form/TagifyInput/TagifyInput.stories.js | 7 +- .../form/TagifyInput/TagifyInput.styles.js | 23 +- .../form/TagifyInput/tagify/react.tagify.js | 298 +- .../src/form/TagifyInput/tagify/tagify.js | 2886 ++++++++++------- .../src/form/TagsInput/TagsInput.js | 94 +- .../components/src/hooks/useResizeObserver.js | 6 +- packages/components/src/index.js | 2 +- .../ActivityAccordion/ActivityAccordion.js | 14 +- .../ActivityAnswersBar/ActivityAnswersBar.js | 22 +- .../ActivityCountdown/ActivityCountdown.js | 20 +- .../src/informative/Avatar/Avatar.js | 6 +- .../informative/AvatarsGroup/AvatarsGroup.js | 4 +- .../components/src/informative/Badge/Badge.js | 15 +- .../src/informative/Badge/Badge.styles.js | 6 +- .../informative/ChatMessage/ChatMessage.js | 9 +- .../FileItemDisplay/FileItemDisplay.js | 3 +- .../HorizontalTimeline/HorizontalTimeline.js | 26 +- .../src/informative/Kanban/Kanban.js | 24 +- .../PaginatedList/PaginatedList.js | 13 +- .../PaginatedList/views/GridItemRender.js | 31 +- .../PaginatedList/views/GridView.js | 14 +- .../PaginatedList/views/TableItemRender.js | 39 +- .../PaginatedList/views/TableView.js | 47 +- .../src/informative/ScoresBar/ScoresBar.js | 34 +- .../informative/SortableList/SortableList.js | 66 +- .../components/DraggableDefault.js | 60 +- .../Swiper/NavigationElements/NextElement.js | 9 +- .../Swiper/NavigationElements/PrevElement.js | 9 +- .../src/informative/Swiper/Swiper.js | 28 +- .../components/src/informative/Table/Table.js | 52 +- .../informative/Table/TableCell/TableCell.js | 53 +- .../src/informative/UserCards/UserCards.js | 16 +- .../UserDisplayItem/UserDisplayItem.js | 35 +- .../UserDisplayItemList.js | 46 +- packages/components/src/informative/index.js | 1 - .../src/layout/PageHeader/PageHeader.js | 17 +- .../TitleTextInput/TitleTextInput.js | 5 +- .../components/src/misc/FileIcon/FileIcon.js | 23 +- packages/components/src/misc/HeroBg/HeroBg.js | 23 +- .../src/misc/ImageLoader/ImageLoader.js | 13 +- .../src/misc/InlineSvg/InlineSvg.js | 12 +- .../src/navigation/Anchor/Anchor.js | 3 +- .../components/src/navigation/Anchor/index.js | 4 +- .../src/navigation/Breadcrumbs/Breadcrumbs.js | 49 +- .../HorizontalStepper/HorizontalStepper.js | 38 +- .../src/navigation/MainNav/MainNav.js | 67 +- .../MainNav/MainNavItem/MainNavItem.js | 31 +- .../components/src/navigation/Menu/Menu.js | 24 +- .../components/src/navigation/Pager/Pager.js | 24 +- .../src/navigation/Spotlight/Spotlight.js | 16 +- .../navigation/Spotlight/SpotlightAction.js | 38 +- .../src/navigation/Spotlight/index.js | 2 +- .../src/navigation/Stepper/Stepper.js | 34 +- .../src/navigation/SubNav/SubNav.js | 32 +- .../navigation/Tabs/TabNavList/Dropdown.js | 16 +- .../navigation/Tabs/TabNavList/TabNavList.js | 28 +- .../Tabs/TabPanelList/TabPanelList.js | 9 +- .../components/src/navigation/Tabs/Tabs.js | 30 +- .../src/navigation/Tree/NodeActions.js | 2 +- .../src/navigation/Tree/NodeRenderer.js | 86 +- .../components/src/navigation/Tree/Tree.js | 194 +- .../VerticalStepper/Progress/Progress.js | 3 +- .../StepperButton/StepperButton.js | 14 +- .../VerticalStepper/VerticalStepper.js | 2 +- .../VerticalStepper/VerticalStepper.styles.js | 20 +- .../src/navigation/VerticalStepper/index.js | 3 - .../src/overlay/ContextHelp/ContextHelp.js | 10 +- .../src/overlay/DetailPanel/DetailPanel.js | 29 +- .../components/src/overlay/Drawer/Drawer.js | 12 +- .../src/overlay/DrawerPush/DrawerPush.js | 5 +- .../src/overlay/Dropdown/Dropdown.js | 7 +- .../src/overlay/Dropdown/Item/Item.js | 15 +- .../components/src/overlay/Dropdown/index.js | 1 - .../src/overlay/EditPanel/EditPanel.js | 39 +- .../overlay/LoadingOverlay/LoadingOverlay.js | 9 +- .../components/src/overlay/Modal/Modal.js | 1 + .../overlay/ModalZoom/ModalZoom.constants.js | 7 +- .../src/overlay/ModalZoom/ModalZoom.js | 4 +- .../overlay/ModalZoom/ModalZoom.stories.js | 18 +- .../ModalsProvider.constants.js | 1 + .../overlay/ModalsProvider/ModalsProvider.js | 24 +- .../components/src/overlay/Popover/Popover.js | 13 +- packages/components/src/theme.mixins.js | 3 +- packages/components/src/tokens.compiled.js | 45 +- .../src/typography/HtmlText/HtmlText.js | 2 +- .../src/typography/Paragraph/Paragraph.js | 8 +- .../components/src/typography/Text/Text.js | 13 +- .../src/typography/TextClamp/TextClamp.js | 1 + .../components/src/typography/Title/Title.js | 5 +- .../components/src/typography/Title/index.js | 4 +- 113 files changed, 3334 insertions(+), 2324 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index f07838d2a..9e2d2c7d7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -44,6 +44,8 @@ ], "import/no-names-as-default": "off", "import/prefer-default-export": "warn", - "no-param-reassign": "off" + "no-param-reassign": "off", + "radix": "off", + "no-unused-vars": "warn" } } diff --git a/merged_tokens.json b/merged_tokens.json index aa0f071fd..66da4e15a 100644 --- a/merged_tokens.json +++ b/merged_tokens.json @@ -2348,7 +2348,7 @@ "y": "0", "blur": "4", "spread": "0", - "color": "rgba({button.background.color.primary.hover}, 0.80)", + "color": "{button.background.color.primary.hover}", "type": "dropShadow" }, "type": "boxShadow" @@ -3174,7 +3174,7 @@ "y": "0", "blur": "4", "spread": "0", - "color": "rgba({buttonIcon.background.color.primary.hover}, 0.80)", + "color": "{buttonAction.background.color.primary.hover--reverse-transparent}", "type": "dropShadow" }, "type": "boxShadow" diff --git a/packages/components/src/ThemeProvider.js b/packages/components/src/ThemeProvider.js index f42265241..668eaa0a0 100644 --- a/packages/components/src/ThemeProvider.js +++ b/packages/components/src/ThemeProvider.js @@ -32,26 +32,27 @@ export const BUBBLES_THEME = { const THEME_PROVIDER_PROP_TYPES = { theme: PropTypes.object, + children: PropTypes.node, }; const THEME_PROVIDER_DEFAULT_PROPS = { theme: BUBBLES_THEME, }; -const parseTheme = (theme) => { - if (isEmpty(theme.other)) return theme; - recursiveParse(theme.other); - return theme; -}; - const recursiveParse = (object) => { if (!isObject(object)) return; - for (const property in object) { + Object.keys(object).forEach((property) => { if (object[property].value) { object[property] = object[property].value; } else { recursiveParse(object[property]); } - } + }); +}; + +const parseTheme = (theme) => { + if (isEmpty(theme.other)) return theme; + recursiveParse(theme.other); + return theme; }; const ThemeProvider = ({ children, theme }) => { diff --git a/packages/components/src/dates/Calendar/Calendar.js b/packages/components/src/dates/Calendar/Calendar.js index bf5764ac7..ff05c7577 100644 --- a/packages/components/src/dates/Calendar/Calendar.js +++ b/packages/components/src/dates/Calendar/Calendar.js @@ -83,6 +83,7 @@ export const CALENDAR_PROP_TYPES = { firstDayOfWeek: PropTypes.oneOf(CALENDAR_FIRST_DAYS), minDate: PropTypes.instanceOf(Date), maxDate: PropTypes.instanceOf(Date), + size: PropTypes.string, }; const Calendar = forwardRef(({ range, size, locale, ...props }, ref) => { @@ -105,6 +106,7 @@ const Calendar = forwardRef(({ range, size, locale, ...props }, ref) => { return ; }); +Calendar.displayName = 'Calendar'; Calendar.defaultProps = CALENDAR_DEFAULT_PROPS; Calendar.propTypes = CALENDAR_PROP_TYPES; diff --git a/packages/components/src/feedback/Alert/Alert.js b/packages/components/src/feedback/Alert/Alert.js index 8dd9cb09d..a1688a39e 100644 --- a/packages/components/src/feedback/Alert/Alert.js +++ b/packages/components/src/feedback/Alert/Alert.js @@ -10,7 +10,8 @@ import { } from '@bubbles-ui/icons/solid'; import { RemoveIcon } from '@bubbles-ui/icons/outline'; import { AlertStyles } from './Alert.styles'; -import { Button, ActionButton } from '../../form'; +import { Button } from '../../form/Button'; +import { ActionButton } from '../../form/ActionButton'; export const ALERT_SEVERITIES = ['info', 'success', 'warning', 'error']; export const ALERT_VARIANTS = ['inline', 'block']; @@ -38,9 +39,9 @@ const Alert = ({ severity = ALERT_SEVERITIES.includes(severity) ? severity : 'info'; const isCloseable = useMemo( () => closeable && !isNil(closeable) && closeable !== '', - [closeable] + [closeable], ); - const { classes, cx } = AlertStyles({ variant, severity }, { name: 'Alert' }); + const { classes } = AlertStyles({ variant, severity }, { name: 'Alert' }); return ( { +const ScoreFeedback = ({ calification, children, styles, className, useAria }) => { const { classes, cx } = ScoreFeedbackStyles({ styles }, { name: 'ScoreFeedback' }); return ( // Role is left empty, it should be 'comment' but the role is proposed for WAI-ARIA 1.3, which is still being drafted. - + { - const { classes, cx } = ScoreFronstageStyles({}, { name: 'ScoreFronstage' }); + const { classes } = ScoreFronstageStyles({}, { name: 'ScoreFronstage' }); return ( diff --git a/packages/components/src/form/FileUpload/FileUpload.js b/packages/components/src/form/FileUpload/FileUpload.js index 921535ba6..eb5091496 100644 --- a/packages/components/src/form/FileUpload/FileUpload.js +++ b/packages/components/src/form/FileUpload/FileUpload.js @@ -2,14 +2,16 @@ import React, { useMemo, useRef } from 'react'; import PropTypes from 'prop-types'; import { Group, Text } from '@mantine/core'; import { Dropzone as MantineDropzone } from '@mantine/dropzone'; -import { FileUploadStyles } from './FileUpload.styles'; -import { Stack, Box } from '../../layout/'; -import { FileItemDisplay } from '../../informative/FileItemDisplay'; -import { Alert } from '../../feedback/Alert'; -import { InputWrapper, INPUT_WRAPPER_PROP_TYPES, ActionButton, Button } from '../../form'; import { DeleteBinIcon } from '@bubbles-ui/icons/solid/'; import { SynchronizeArrowIcon } from '@bubbles-ui/icons/outline'; import { isEmpty, isFunction } from 'lodash'; +import { FileUploadStyles } from './FileUpload.styles'; +import { Stack, Box } from '../../layout'; +import { FileItemDisplay } from '../../informative/FileItemDisplay'; +import { Alert } from '../../feedback/Alert'; +import { InputWrapper, INPUT_WRAPPER_PROP_TYPES } from '../InputWrapper'; +import { ActionButton } from '../ActionButton'; +import { Button } from '../Button'; export const FILE_UPLOAD_DEFAULT_PROPS = { disabled: false, @@ -69,14 +71,14 @@ const FileUpload = ({ const onDropHandler = (acceptedFiles) => { const newFiles = [...files, ...acceptedFiles]; - isFunction(onChange) && onChange(newFiles.length === 1 ? newFiles[0] : newFiles); + if (isFunction(onChange)) onChange(newFiles.length === 1 ? newFiles[0] : newFiles); setFiles(newFiles); setError(false); }; const removeFile = (index) => { const newFiles = files.filter((file, fileIndex) => fileIndex !== index); - isFunction(onChange) && onChange(newFiles.length === 1 ? newFiles[0] : newFiles); + if (isFunction(onChange)) onChange(newFiles.length === 1 ? newFiles[0] : newFiles); setFiles(newFiles); }; @@ -94,7 +96,7 @@ const FileUpload = ({ const { classes, cx } = FileUploadStyles( { disabled, single, files, hasError }, - { name: 'FileUpload' } + { name: 'FileUpload' }, ); return ( diff --git a/packages/components/src/form/FileUpload/index.js b/packages/components/src/form/FileUpload/index.js index bb3059df5..fca98b5be 100644 --- a/packages/components/src/form/FileUpload/index.js +++ b/packages/components/src/form/FileUpload/index.js @@ -1,3 +1 @@ -import { FileUpload } from './FileUpload'; - -export { FileUpload }; +export * from './FileUpload'; diff --git a/packages/components/src/form/ImagePreviewInput/ImagePreviewInput.js b/packages/components/src/form/ImagePreviewInput/ImagePreviewInput.js index 8c8bae5ae..6b0168ea5 100644 --- a/packages/components/src/form/ImagePreviewInput/ImagePreviewInput.js +++ b/packages/components/src/form/ImagePreviewInput/ImagePreviewInput.js @@ -2,8 +2,9 @@ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { isFunction, isString, isNil } from 'lodash'; import { CloudUploadIcon, UndoIcon } from '@bubbles-ui/icons/outline/'; -import { Box, Stack } from '../../layout'; -import { Button } from '../../form'; +import { Box } from '../../layout/Box'; +import { Stack } from '../../layout/Stack'; +import { Button } from '../Button'; import { ImageLoader } from '../../misc/ImageLoader'; import { ImagePreviewInputStyles } from './ImagePreviewInput.styles'; @@ -24,6 +25,9 @@ export const IMAGE_PREVIEW_INPUT_PROP_TYPES = { previewStyle: PropTypes.object, control: PropTypes.element, onChange: PropTypes.func, + readonly: PropTypes.bool, + disabled: PropTypes.bool, + useAria: PropTypes.bool, }; const ImagePreviewInput = ({ @@ -36,7 +40,6 @@ const ImagePreviewInput = ({ readonly, disabled, useAria, - ...props }) => { const [imagePreview, setImagePreview] = useState(previewURL); const [imageValue, setImageValue] = useState(value); @@ -57,20 +60,20 @@ const ImagePreviewInput = ({ }, [value]); const resetImage = () => { - isFunction(onChange) && onChange(null); + if (isFunction(onChange)) onChange(null); setImagePreview(null); setImageValue(null); }; const openFileBrowser = () => { - let input = document.createElement('input'); + const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.onchange = (e) => { const file = e.target.files[0]; file.path = file.name; setImageValue(file); - isFunction(onChange) && onChange(file); + if (isFunction(onChange)) onChange(file); }; input.click(); }; diff --git a/packages/components/src/form/InputDescription/InputDescription.js b/packages/components/src/form/InputDescription/InputDescription.js index 15395c284..f72092576 100644 --- a/packages/components/src/form/InputDescription/InputDescription.js +++ b/packages/components/src/form/InputDescription/InputDescription.js @@ -1,15 +1,15 @@ import React from 'react'; import { Text } from '@mantine/core'; +import { AlertInformationCircleIcon } from '@bubbles-ui/icons/solid'; +import { Box } from '../../layout/Box'; import { INPUT_DESCRIPTION_DEFAULT_PROPS, INPUT_DESCRIPTION_PROP_TYPES, } from './InputDescription.constants'; import { InputDescriptionStyles } from './InputDescription.styles'; -import { AlertInformationCircleIcon } from '@bubbles-ui/icons/solid'; -import { Box } from '../../layout'; -const InputDescription = ({ message, withIcon, ...props }) => { - const { classes, cx } = InputDescriptionStyles({ withIcon }); +const InputDescription = ({ message, withIcon }) => { + const { classes } = InputDescriptionStyles({ withIcon }); return ( diff --git a/packages/components/src/form/InputDescription/index.js b/packages/components/src/form/InputDescription/index.js index 5c7c703bf..264c3cb95 100644 --- a/packages/components/src/form/InputDescription/index.js +++ b/packages/components/src/form/InputDescription/index.js @@ -1,3 +1 @@ -import { InputDescription } from './InputDescription'; - -export { InputDescription }; +export * from './InputDescription'; diff --git a/packages/components/src/form/InputLabel/InputLabel.js b/packages/components/src/form/InputLabel/InputLabel.js index 5e6fcf881..824c2c406 100644 --- a/packages/components/src/form/InputLabel/InputLabel.js +++ b/packages/components/src/form/InputLabel/InputLabel.js @@ -1,13 +1,13 @@ import React from 'react'; +import { isEmpty } from 'lodash'; +import { Box } from '../../layout/Box'; +import { Text } from '../../typography/Text'; +import { InputDescription } from '../InputDescription'; import { InputLabelStyles } from './InputLabel.styles'; import { INPUT_LABEL_DEFAULT_PROPS, INPUT_LABEL_PROP_TYPES } from './InputLabel.constants'; -import { Text } from '../../typography'; -import { InputDescription } from '../InputDescription'; -import { isEmpty } from 'lodash'; -import { Box } from '../../layout'; const InputLabel = ({ label, description, withDescriptionIcon, required, ...props }) => { - const { classes, cx } = InputLabelStyles({}, { name: 'InputLabel' }); + const { classes } = InputLabelStyles({}, { name: 'InputLabel' }); return ( diff --git a/packages/components/src/form/InputWrapper/InputWrapper.js b/packages/components/src/form/InputWrapper/InputWrapper.js index 194311053..30aba2a7e 100644 --- a/packages/components/src/form/InputWrapper/InputWrapper.js +++ b/packages/components/src/form/InputWrapper/InputWrapper.js @@ -4,6 +4,7 @@ import { Box } from '../../layout/Box'; import { Stack } from '../../layout/Stack'; import { InputError } from '../InputError'; import { InputHelp } from '../InputHelp'; +import { InputLabel } from '../InputLabel'; import { InputWrapperStyles } from './InputWrapper.styles'; import { INPUT_WRAPPER_DEFAULT_PROPS, @@ -11,7 +12,6 @@ import { INPUT_WRAPPER_ORIENTATIONS, INPUT_WRAPPER_SIZES, } from './InputWrapper.constants'; -import { InputLabel } from '../InputLabel'; const InputWrapper = ({ orientation: orientationProp, diff --git a/packages/components/src/form/MultiSelect/MultiSelect.js b/packages/components/src/form/MultiSelect/MultiSelect.js index f61e01773..b37472b60 100644 --- a/packages/components/src/form/MultiSelect/MultiSelect.js +++ b/packages/components/src/form/MultiSelect/MultiSelect.js @@ -1,31 +1,35 @@ import React, { forwardRef, useEffect, useMemo, useRef } from 'react'; -import { MultiSelectStyles } from './MultiSelect.styles'; +import Proptypes from 'prop-types'; import { find, isArray, isEmpty, isFunction, isString } from 'lodash'; import { MultiSelect as MantineMultiSelect } from '@mantine/core'; -import { ActionButton } from '../ActionButton'; import { ChevDownIcon, RemoveIcon } from '@bubbles-ui/icons/outline'; -import { InputWrapper } from '../InputWrapper'; import { useId } from '@mantine/hooks'; -import { Badge } from '../../informative'; +import { ActionButton } from '../ActionButton'; +import { InputWrapper } from '../InputWrapper'; +import { Badge } from '../../informative/Badge'; +import { Box } from '../../layout/Box'; +import { Dropdown, Item } from '../../overlay/Dropdown'; +import { MultiSelectStyles } from './MultiSelect.styles'; import { MULTI_SELECT_DEFAULT_PROPS, MULTI_SELECT_ORIENTATIONS, MULTI_SELECT_PROP_TYPES, MULTI_SELECT_SIZES, } from './MultiSelect.constants'; -import { Box } from '../../layout'; -import { Dropdown, Item } from '../../overlay/Dropdown'; const GetValueComponent = forwardRef( - ({ others: { Component, classNames, onRemove, ...others } }, ref) => { - return ( - - - - ); - } + ({ others: { Component, classNames, onRemove, ...others } }, ref) => ( + + + + ), ); +GetValueComponent.displayName = 'GetValueComponent'; +GetValueComponent.propTypes = { + others: Proptypes.any, +}; + const MultiSelect = forwardRef( ( { @@ -56,7 +60,7 @@ const MultiSelect = forwardRef( style, ...props }, - ref + ref, ) => { const hasIcon = !!icon; const [show, setShow] = React.useState(true); @@ -115,9 +119,9 @@ const MultiSelect = forwardRef( // ······················································ // STYLES - const { classes, cx } = MultiSelectStyles( + const { classes } = MultiSelectStyles( { size, multiple, rightEvents: isClearable && showClear, hasIcon }, - { name: 'MultiSelect' } + { name: 'MultiSelect' }, ); return ( @@ -139,10 +143,9 @@ const MultiSelect = forwardRef( const data = find(props.data, { value: v }); if (data) { if (ValueComponent) { - return ; - } else { - return ; + return ; } + return ; } // return null; @@ -200,9 +203,10 @@ const MultiSelect = forwardRef( )} ); - } + }, ); +MultiSelect.displayName = 'MultiSelect'; MultiSelect.defaultProps = MULTI_SELECT_DEFAULT_PROPS; MultiSelect.propTypes = MULTI_SELECT_PROP_TYPES; diff --git a/packages/components/src/form/ProSwitch/ProSwitch.js b/packages/components/src/form/ProSwitch/ProSwitch.js index 736efa3b0..0994c13c0 100644 --- a/packages/components/src/form/ProSwitch/ProSwitch.js +++ b/packages/components/src/form/ProSwitch/ProSwitch.js @@ -1,9 +1,9 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; import { Box } from '@mantine/core'; -import { Switch } from '../../form'; -import { ProSwitchStyles } from './ProSwitch.styles'; import { isFunction } from 'lodash'; +import { Switch } from '../Switch'; +import { ProSwitchStyles } from './ProSwitch.styles'; export const PRO_SWITCH_DEFAULT_PROPS = { onChange: () => {}, @@ -17,6 +17,8 @@ export const PRO_SWITCH_PROP_TYPES = { checked: PropTypes.bool, ariaLabel: PropTypes.string, useAria: PropTypes.bool, + onChange: PropTypes.func, + classNames: PropTypes.any, }; const ProSwitch = forwardRef( @@ -31,9 +33,9 @@ const ProSwitch = forwardRef( const [state, setState] = React.useState(checked); - const handleOnChange = (checked) => { - setState(checked); - isFunction(onChange) && onChange(checked); + const handleOnChange = (isChecked) => { + setState(isChecked); + if (isFunction(onChange)) onChange(isChecked); }; React.useEffect(() => { @@ -64,9 +66,10 @@ const ProSwitch = forwardRef( /> ); - } + }, ); +ProSwitch.displayName = 'ProSwitch'; ProSwitch.defaultProps = PRO_SWITCH_DEFAULT_PROPS; ProSwitch.propTypes = PRO_SWITCH_PROP_TYPES; diff --git a/packages/components/src/form/ScoreInput/ScoreInput.js b/packages/components/src/form/ScoreInput/ScoreInput.js index 7de81e677..ae0247d14 100644 --- a/packages/components/src/form/ScoreInput/ScoreInput.js +++ b/packages/components/src/form/ScoreInput/ScoreInput.js @@ -1,11 +1,14 @@ import React, { useState, useEffect, useMemo } from 'react'; -import { Box } from '../../layout'; -import { Text } from '../../typography'; import { isFunction } from 'lodash'; import { useElementSize } from '@mantine/hooks'; -import { ScoreInputStyles } from './ScoreInput.styles'; import { ChevLeftIcon, ChevRightIcon } from '@bubbles-ui/icons/outline'; -import { InputWrapper, TextInput, NumberInput, Select } from '../'; +import { Box } from '../../layout'; +import { Text } from '../../typography'; +import { ScoreInputStyles } from './ScoreInput.styles'; +import { InputWrapper } from '../InputWrapper'; +import { TextInput } from '../TextInput'; +import { NumberInput } from '../NumberInput'; +import { Select } from '../Select'; import { SCORE_INPUT_DEFAULT_PROPS, SCORE_INPUT_PROP_TYPES } from './ScoreInput.constants'; const ScoreInput = ({ @@ -36,20 +39,25 @@ const ScoreInput = ({ const maxGrades = Math.floor((inputWidth + 2) / 40); const hiddenGrades = useMemo( () => grades.length - (maxGrades - 1) - displacedGrades, - [maxGrades, displacedGrades] + [maxGrades, displacedGrades], ); const isOverflowing = grades.length > maxGrades - 1; const selectedGradeIndex = grades.findIndex(({ score }) => - showLetters ? score === grade.score : score === Math.round(grade.score) + showLetters ? score === grade.score : score === Math.round(grade.score), ); if (!acceptCustom && value?.score && !grades.find(({ score }) => score === value?.score)) { acceptCustom = value?.letter ? 'text' : 'number'; } - const onChangeHandler = (grade) => { - setGrade(grade); - isFunction(onChange) && onChange(grade); + const { classes, cx } = ScoreInputStyles( + { error, gradeWidth, selectedGradeIndex, displacedGrades, gradesLength: grades.length }, + { name: 'ScoreInput' }, + ); + + const onChangeHandler = (newGrade) => { + setGrade(newGrade); + if (isFunction(onChange)) onChange(newGrade); }; const renderCustomInput = () => { @@ -58,11 +66,11 @@ const ScoreInput = ({ + onChange={(newValue) => onChangeHandler({ - score: grades.find(({ letter }) => letter?.toLowerCase() === value.toLowerCase()) + score: grades.find(({ letter }) => letter?.toLowerCase() === newValue.toLowerCase()) ?.score, - letter: value.toUpperCase(), + letter: newValue.toUpperCase(), }) } ariaLabel={customLabel} @@ -77,16 +85,17 @@ const ScoreInput = ({ value={grade?.score} precision={decimalPrecision} decimalSeparator={decimalSeparator === 'dot' ? '.' : ','} - onChange={(value) => + onChange={(newValue) => onChangeHandler({ - score: value, - letter: grades.find(({ score }) => score === value)?.letter || selectValue, + score: newValue, + letter: grades.find(({ score }) => score === newValue)?.letter || selectValue, }) } ariaLabel={customLabel} /> ); } + return null; }; const handleInputResize = () => { @@ -102,12 +111,8 @@ const ScoreInput = ({ if (parentWidth >= widthWithSpace) { if (inputMaxWidth !== widthWithSpace) setInputMaxWidth(widthWithSpace); if (hiddenGrades <= 0) setDisplacedGrades(displacedGrades + hiddenGrades - 1); - } else { - if (inputMaxWidth !== widthWithoutSpace) setInputMaxWidth(widthWithoutSpace); - } - } else { - if (inputMaxWidth !== 10000) setInputMaxWidth(10000); - } + } else if (inputMaxWidth !== widthWithoutSpace) setInputMaxWidth(widthWithoutSpace); + } else if (inputMaxWidth !== 10000) setInputMaxWidth(10000); }; const handleDisplaceToLeftGrades = () => { @@ -122,7 +127,7 @@ const ScoreInput = ({ if (displacedGrades < maxGrades) { setDisplacedGrades(0); } else { - setDisplacedGrades((displacedGrades) => displacedGrades - maxGrades + 2); + setDisplacedGrades((newGrades) => newGrades - maxGrades + 2); } }; @@ -130,8 +135,9 @@ const ScoreInput = ({ const isLeftToRight = direction === 'ltr'; const sortedGrades = grades.sort((a, b) => - !isLeftToRight ? b?.score - a?.score : a?.score - b?.score + !isLeftToRight ? (b?.score ?? 0) - (a?.score ?? 0) : (a?.score ?? 0) - (b?.score ?? 0), ); + const gradesToReturn = sortedGrades.map((arrayGrade) => { const isSelected = arrayGrade.score === grade.score; return ( @@ -162,7 +168,7 @@ const ScoreInput = ({ role={useAria ? 'button' : undefined} > - + , ); } if (hiddenGrades > 0) { @@ -177,20 +183,19 @@ const ScoreInput = ({ role={useAria ? 'button' : undefined} > - + , ); } return gradesToReturn; }; + // -------------------------------------------------------------- + // EFFECTS + useEffect(() => { handleInputResize(); }, [inputWidth, parentWidth, inputMaxWidth]); - const { classes, cx } = ScoreInputStyles( - { error, gradeWidth, selectedGradeIndex, displacedGrades, gradesLength: grades.length }, - { name: 'ScoreInput' } - ); return ( @@ -207,13 +212,13 @@ const ScoreInput = ({ }))} placeholder={placeholder} value={selectValue} - onChange={(value) => { - const parsedValue = parseFloat(value); + onChange={(val) => { + const parsedValue = parseFloat(val); onChangeHandler({ score: parsedValue, letter: tags.find((tag) => tag.score === parsedValue)?.letter, }); - setSelectValue(value); + setSelectValue(val); }} /> diff --git a/packages/components/src/form/Select/Select.js b/packages/components/src/form/Select/Select.js index 1236dd4e9..f67a355d8 100644 --- a/packages/components/src/form/Select/Select.js +++ b/packages/components/src/form/Select/Select.js @@ -1,15 +1,15 @@ import React, { forwardRef, useEffect, useMemo, useState } from 'react'; import { ChevDownIcon, RemoveIcon } from '@bubbles-ui/icons/outline'; import { Select as MantineSelect } from '@mantine/core'; -import { isEmpty, isFunction, isNil, isString, map } from 'lodash'; -import { SELECT_PROP_TYPES, SELECT_DEFAULT_PROPS } from './Select.constants'; import { useId } from '@mantine/hooks'; +import { isEmpty, isFunction, isNil, isString, map } from 'lodash'; import { InputWrapper } from '../InputWrapper'; import { ActionButton } from '../ActionButton'; import { SelectStyles } from './Select.styles'; import { Paragraph } from '../../typography'; import { MultiSelect } from '../MultiSelect'; import { Dropdown, Item } from '../../overlay/Dropdown'; +import { SELECT_PROP_TYPES, SELECT_DEFAULT_PROPS } from './Select.constants'; const Select = forwardRef( ( @@ -48,7 +48,7 @@ const Select = forwardRef( withinPortal, ...props }, - ref + ref, ) => { const data = map(_data, (d) => (isString(d) ? d : { ...d, value: d?.value.toString() })); const value = isNil(_value) ? _value : _value.toString(); @@ -98,9 +98,9 @@ const Select = forwardRef( // ······················································ // STYLES - const { classes, cx } = SelectStyles( + const { classes } = SelectStyles( { size, rightEvents: isClearable && showClear, variant }, - { name: 'Select' } + { name: 'Select' }, ); return valueComponent ? ( @@ -185,9 +185,10 @@ const Select = forwardRef( )} ); - } + }, ); +Select.displayName = 'Select'; Select.defaultProps = SELECT_DEFAULT_PROPS; Select.propTypes = SELECT_PROP_TYPES; diff --git a/packages/components/src/form/TableInput/TableInput.js b/packages/components/src/form/TableInput/TableInput.js index 59407835f..09e01df82 100644 --- a/packages/components/src/form/TableInput/TableInput.js +++ b/packages/components/src/form/TableInput/TableInput.js @@ -1,6 +1,6 @@ import React, { useEffect, useMemo, useState } from 'react'; import update from 'immutability-helper'; -import { isEmpty, isFunction } from 'lodash'; +import { map, forEach, isEmpty, isFunction } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; import { useForm } from 'react-hook-form'; import { Box } from '../../layout/Box'; @@ -77,7 +77,7 @@ const TableInput = ({ const parseItem = (item) => { const result = {}; - _.forEach(props.columns, ({ accessor }) => { + forEach(props.columns, ({ accessor }) => { result[accessor] = item[accessor]; }); return result; @@ -85,9 +85,7 @@ const TableInput = ({ const handleOnAdd = async (item) => { if (unique) { - const values = _.map(tableData, (d) => { - return JSON.stringify(parseItem(d)); - }); + const values = map(tableData, (d) => JSON.stringify(parseItem(d))); if (values.includes(JSON.stringify(parseItem(item)))) { return; } diff --git a/packages/components/src/form/TableInput/TableInputDisplay.js b/packages/components/src/form/TableInput/TableInputDisplay.js index 24753dc9e..edc98fe69 100644 --- a/packages/components/src/form/TableInput/TableInputDisplay.js +++ b/packages/components/src/form/TableInput/TableInputDisplay.js @@ -1,14 +1,13 @@ import React, { useCallback, useState } from 'react'; import PropTypes from 'prop-types'; import { find, isFunction } from 'lodash'; -import { useTable, useExpanded } from 'react-table'; +import { useTable } from 'react-table'; import { Controller } from 'react-hook-form'; import { AddIcon } from '@bubbles-ui/icons/outline'; import { DragDropContext, Droppable } from 'react-beautiful-dnd'; import { Text } from '../../typography/Text'; import { TableStyles } from '../../informative/Table/Table.styles'; import { TABLE_INPUT_DEFAULT_PROPS, TABLE_INPUT_PROP_TYPES } from './TableInput.constants'; -import { Button } from '../Button'; import { TableInputRow } from './TableInputRow'; import { ActionButton } from '../ActionButton'; @@ -102,7 +101,7 @@ const TableInputDisplay = ({ return null; }, - [columns, disabled, errors, formValues] + [columns, disabled, errors, formValues], ); const handleDragEnd = (result) => { @@ -126,11 +125,12 @@ const TableInputDisplay = ({ > {!!showHeaders && - headerGroups.map((headerGroup) => ( - + headerGroups.map((headerGroup, i) => ( + {(sortable && !disabled) || forceSortable ? : null} - {headerGroup.headers.map((column) => ( + {headerGroup.headers.map((column, j) => ( {(provided, snapshot) => ( - {rows.map((row, i) => { + {rows.map((row, k) => { prepareRow(row); return ( { if (editing && cell.column.editable !== false) { - const { column, row } = cell; - const fieldName = `${row.original.tableInputRowId}.${column.id}`; + const { column, row: _row } = cell; + const fieldName = `${_row.original.tableInputRowId}.${column.id}`; let node = null; let rules = []; let inputProps = {}; @@ -63,7 +65,7 @@ const TableInputRow = ({ } else { node = column.input.node; rules = column.input.rules; - let { node: _, rules: __, ..._inputProps } = column.input; + const { node: _, rules: __, ..._inputProps } = column.input; inputProps = _inputProps; } @@ -90,6 +92,11 @@ const TableInputRow = ({ return ; }; + const cancelEditing = () => { + setEditing(false); + if (isFunction(onEditing)) onEditing(false); + }; + const handleOnEdit = async () => { const result = await trigger(); if (result) { @@ -107,11 +114,6 @@ const TableInputRow = ({ if (isFunction(onEditing)) onEditing(true); }; - const cancelEditing = () => { - setEditing(false); - if (isFunction(onEditing)) onEditing(false); - }; - return ( - {(provided, snapshot) => { - return ( - <> - - {sortable && ( - - - - - - )} - {row.cells.map((cell) => ( - ( + <> + + {sortable && ( + + - {getColumCellValue(cell)} - - ))} + + + + )} + {row.cells.map((cell, i) => ( - {editing ? ( + {getColumCellValue(cell)} + + ))} + + {editing ? ( + <> + } + tooltip={labels.accept || 'Accept'} + onClick={handleOnEdit} + /> + } + tooltip={labels.cancel || 'Cancel'} + onClick={cancelEditing} + /> + + ) : ( + row.original.editable !== false && ( <> - } - tooltip={labels.accept || 'Accept'} - onClick={handleOnEdit} - /> - } - tooltip={labels.cancel || 'Cancel'} - onClick={cancelEditing} - /> + {addable && ( + } + tooltip={labels.add || 'Add'} + onClick={() => onItemAdd(row)} + /> + )} + {editable && ( + } + tooltip={labels.edit || 'Edit'} + onClick={initEditing} + /> + )} + {removable && ( + } + tooltip={labels.remove || 'Remove'} + onClick={() => onRemove(index)} + /> + )} - ) : ( - row.original.editable !== false && ( - <> - {addable && ( - } - tooltip={labels.add || 'Add'} - onClick={() => onItemAdd(row)} - /> - )} - {editable && ( - } - tooltip={labels.edit || 'Edit'} - onClick={initEditing} - /> - )} - {removable && ( - } - tooltip={labels.remove || 'Remove'} - onClick={() => onRemove(index)} - /> - )} - - ) - )} + ) + )} + + + {rowsExpanded?.includes(row.id) ? ( + + + {renderRowSubComponent({ row })} - {rowsExpanded?.includes(row.id) ? ( - - - {renderRowSubComponent({ row })} - - - ) : null} - - ); - }} + ) : null} + + )} ); }; +TableInputRow.propTypes = { + labels: PropTypes.object, + row: PropTypes.object, + index: PropTypes.number, + onItemAdd: PropTypes.func, + onRemove: PropTypes.func, + classes: PropTypes.object, + tableClasses: PropTypes.object, + visibleColumns: PropTypes.array, + cx: PropTypes.func, + totalRows: PropTypes.number, + sortable: PropTypes.bool, + editable: PropTypes.bool, + addable: PropTypes.bool, + removable: PropTypes.bool, + disabled: PropTypes.bool, + rowsExpanded: PropTypes.array, + editing: PropTypes.bool, + onEditing: PropTypes.func, + onEdit: PropTypes.func, + renderRowSubComponent: PropTypes.func, + onChangeRow: PropTypes.func, +}; + export { TableInputRow }; diff --git a/packages/components/src/form/TagifyInput/TagifyInput.js b/packages/components/src/form/TagifyInput/TagifyInput.js index 5fd83f75a..09a26a98b 100644 --- a/packages/components/src/form/TagifyInput/TagifyInput.js +++ b/packages/components/src/form/TagifyInput/TagifyInput.js @@ -80,9 +80,10 @@ const TagifyInput = forwardRef( mixed, error, size, + ariaLabel, ...props }, - ref + ref, ) => { const uuid = useId(); const { classes, cx } = TagifyInputStyles({ size, error }, { name: 'TagifyInput' }); @@ -117,7 +118,6 @@ const TagifyInput = forwardRef( onDropdownUpdated={onDropdownUpdated} readOnly={readOnly} disabled={disabled} - children={children} settings={settings} InputMode={InputMode} autoFocus={autoFocus} @@ -128,15 +128,17 @@ const TagifyInput = forwardRef( showDropdown={showDropdown} withSuggestions={withSuggestions} amountOfDuplicates={amountOfDuplicates} - ariaLabel={props.label || ariaLabel} - /> + ariaLabel={props.label ?? ariaLabel} + > + {children} + ); - } + }, ); +TagifyInput.displayName = 'TagifyInput'; TagifyInput.defaultProps = TAGIFY_DEFAULT_PROPS; - TagifyInput.propTypes = { ...INPUT_WRAPPER_PROP_TYPES, name: PropTypes.string, diff --git a/packages/components/src/form/TagifyInput/TagifyInput.stories.js b/packages/components/src/form/TagifyInput/TagifyInput.stories.js index 7b5acd8f6..49f90cb37 100644 --- a/packages/components/src/form/TagifyInput/TagifyInput.stories.js +++ b/packages/components/src/form/TagifyInput/TagifyInput.stories.js @@ -20,9 +20,7 @@ export default { }, }; -const Template = ({ ...props }) => { - return ; -}; +const Template = ({ ...props }) => ; export const Playground = Template.bind({}); @@ -32,7 +30,8 @@ Playground.args = { description: 'Include some tags in your text', error: '', help: 'To add a tag, type @ and a character to show the tag list', - value: '', + value: + 'hola [[{"id":100,"value":"Subject:Numering","title":"Subject:Numering","prefix":"@"}]] ahh pues flipa [[{"id":105,"value":"Stage:Code","title":"Stage:Code","prefix":"@"}]]', settings: { mode: 'mix', pattern: /@/, // <- must define "patten" in mixed mode diff --git a/packages/components/src/form/TagifyInput/TagifyInput.styles.js b/packages/components/src/form/TagifyInput/TagifyInput.styles.js index f2224ba82..b0718e76c 100644 --- a/packages/components/src/form/TagifyInput/TagifyInput.styles.js +++ b/packages/components/src/form/TagifyInput/TagifyInput.styles.js @@ -1,14 +1,8 @@ import { createStyles } from '@mantine/styles'; -import { - pxToRem, - getPaddings, - getFontExpressive, - getFontProductive, - getFocusStyles, -} from '../../theme.mixins'; +import { getFontProductive } from '../../theme.mixins'; import { getInputSizes, getInputStyle } from '../mixins/fieldStyles.mixins'; -export const TagifyInputStyles = createStyles((theme, { size }) => { +const TagifyInputStyles = createStyles((theme, { size }) => { const fontSizes = { xs: theme.fontSizes['1'], sm: theme.fontSizes['2'], @@ -19,10 +13,11 @@ export const TagifyInputStyles = createStyles((theme, { size }) => { root: { marginBottom: theme.spacing['1'], '&.tagify': { - ...getFontProductive(null, 400), + border: 0, borderRadius: 4, borderColor: theme.colors.ui01, color: theme.colors.text02, + lineHeight: 0, }, '& .tagify__input': { ...getInputSizes(size || 'md', inputTheme.spacing.padding, false), @@ -36,17 +31,27 @@ export const TagifyInputStyles = createStyles((theme, { size }) => { // boxShadow: `0 0 0 3px ${theme.colors.interactive03h}`, }, + '.tagify__tag > div': { + ...getFontProductive(14, 400), + }, + '.tagify__tag > div::before': { borderRadius: 9999, height: 20, top: -1, + background: theme.colors.interactive03h, + boxShadow: 'none', }, '.tagify__tag:focus div::before, .tagify__tag:hover:not([readonly]) div::before': { top: -1, right: 0, left: 0, + background: theme.colors.interactive03h, + boxShadow: 'none', }, }, }; }); + +export { TagifyInputStyles }; diff --git a/packages/components/src/form/TagifyInput/tagify/react.tagify.js b/packages/components/src/form/TagifyInput/tagify/react.tagify.js index 164ef2c8f..1ce9615c6 100644 --- a/packages/components/src/form/TagifyInput/tagify/react.tagify.js +++ b/packages/components/src/form/TagifyInput/tagify/react.tagify.js @@ -1,71 +1,72 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; -import { array, bool, func, number, object, oneOfType, string } from 'prop-types'; -import { Box } from '../../../layout'; -import { Badge } from '../../../informative'; +import PropTypes from 'prop-types'; import { isFunction } from 'lodash'; +import { Box } from '../../../layout/Box'; +import { Badge } from '../../../informative/Badge'; import Tagify from './tagify'; const noop = (_) => _; const isSameDeep = (a, b) => { - const trans = (x) => (typeof x == 'string' ? x : JSON.stringify(x)); - return trans(a) == trans(b); + const trans = (x) => (typeof x === 'string' ? x : JSON.stringify(x)); + return trans(a) === trans(b); }; // if a template is a React component, it should be outputed as a String (and not as a React component) function templatesToString(templates) { if (templates) { - for (let templateName in templates) { - let Template = templates[templateName]; - let isReactComp = String(Template).includes('jsxRuntime'); + Object.keys(templates).forEach((templateName) => { + const Template = templates[templateName]; + const isReactComp = String(Template).includes('jsxRuntime'); + if (isReactComp) { templates[templateName] = (...props) => renderToStaticMarkup(