From 4d7e9e8e6e7a5f798d2d26909c08a858ba7e7090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ver=C3=B3nica=20Mil=C3=A1n?= Date: Thu, 15 Jun 2023 15:24:19 +0200 Subject: [PATCH] FeatureSelection widget fixes (#708) --- CHANGELOG.md | 2 + packages/react-ui/src/types.d.ts | 4 ++ .../FeatureSelectionUIDropdown.js | 58 ++++++++++++++----- .../FeatureSelectionUIGeometryChips.js | 37 +++++++++--- .../FeatureSelectionWidgetUI.js | 26 ++++++--- .../FeatureSelectionWidgetUI.stories.js | 26 ++++++++- .../src/widgets/FeatureSelectionWidget.js | 13 ++++- 7 files changed, 131 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d5efa787..ccac22cd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ ## Not released +- FeatureSelection widget fixes [#708](https://github.com/CartoDB/carto-react/pull/708) + ## 2.0 ### 2.0.9 (2023-06-14) diff --git a/packages/react-ui/src/types.d.ts b/packages/react-ui/src/types.d.ts index 2a4bdadf1..ed8f71413 100644 --- a/packages/react-ui/src/types.d.ts +++ b/packages/react-ui/src/types.d.ts @@ -168,6 +168,8 @@ export type FeatureSelectionWidgetUI = { onSelectGeometry?: Function; onDeleteGeometry?: Function; tooltipPlacement?: 'bottom' | 'left' | 'right' | 'top'; + size?: 'small' | 'medium'; + chipLabel?: string; }; export type FeatureSelectionUIDropdown = { @@ -178,6 +180,7 @@ export type FeatureSelectionUIDropdown = { enabled?: boolean; onEnabledChange?: Function; tooltipPlacement?: 'bottom' | 'left' | 'right' | 'top'; + editDisabled?: boolean; }; export type FeatureSelectionUIGeometryChips = { features: GeoJSON.Feature[]; @@ -187,6 +190,7 @@ export type FeatureSelectionUIGeometryChips = { disabledChipTooltip?: string; size?: 'small' | 'medium'; tooltipPlacement?: 'bottom' | 'left' | 'right' | 'top'; + chipLabel?: string; }; export type FeatureSelectionUIToggleButton = { icon: React.ReactNode; diff --git a/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionUIDropdown.js b/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionUIDropdown.js index 6216c6999..8ca0a5795 100644 --- a/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionUIDropdown.js +++ b/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionUIDropdown.js @@ -1,4 +1,4 @@ -import { ArrowDropDown } from '@mui/icons-material'; +import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown'; import { Box, Divider, @@ -14,11 +14,29 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import Typography from '../../components/atoms/Typography'; -const StyledButtonArrow = styled(IconButton)(({ theme: { spacing, palette } }) => ({ - color: palette.text.secondary, - width: spacing(3) +const ArrowButton = styled(IconButton, { + shouldForwardProp: (prop) => prop !== 'isOpen' +})(({ isOpen, theme }) => ({ + color: theme.palette.text.secondary, + width: theme.spacing(3), + transform: `rotate(${isOpen ? '180' : '0'}deg)`, + backgroundColor: isOpen ? theme.palette.action.hover : undefined })); +const SelectionMenuItem = styled(MenuItem, { + shouldForwardProp: (prop) => prop !== 'disabled' +})(({ disabled, theme }) => ({ + ...(disabled && { + pointerEvents: 'none', + color: theme.palette.text.disabled + }) +})); + +const DisabledMenuItem = styled(MenuItem)(({ theme }) => ({ + '&.Mui-disabled': { + opacity: 1 + } +})); /** * Renders a `` component. * This component displays the dropdown layout with all available edit and selection modes @@ -39,6 +57,7 @@ const StyledButtonArrow = styled(IconButton)(({ theme: { spacing, palette } }) = * @param { "bottom" | "left" | "right" | "top" | undefined } [props.tooltipPlacement] * @param {string} [props.tooltipText] * @param {string} [props.menuHeaderText] + * @param {boolean} [props.editDisabled] * --> */ function FeatureSelectionUIDropdown({ @@ -49,7 +68,8 @@ function FeatureSelectionUIDropdown({ enabled, tooltipPlacement, tooltipText = '', - menuHeaderText = '' + menuHeaderText = '', + editDisabled }) { const theme = useTheme(); const [anchorEl, setAnchorEl] = useState(null); @@ -72,34 +92,39 @@ function FeatureSelectionUIDropdown({ }; const showDivider = !!selectionModes.length && !!editModes.length; + const isEditItem = (mode) => editModes.find((editMode) => editMode.id === mode.id); const createMenuItemWrapper = (mode) => ( - handleSelectMode(mode.id)} + disabled={editDisabled && isEditItem(mode)} > {mode.icon} - {capitalize(mode.label)} + + {capitalize(mode.label)} + - + ); return ( <> - - - + + {menuHeaderText && ( - - {menuHeaderText} - + + + {menuHeaderText} + + )} {!!selectionModes.length && selectionModes.map(createMenuItemWrapper)} {showDivider && spacing(1, 0) }} />} @@ -136,7 +163,8 @@ FeatureSelectionUIDropdown.propTypes = { enabled: PropTypes.bool, tooltipPlacement: PropTypes.string, tooltipText: PropTypes.string, - menuHeaderText: PropTypes.string + menuHeaderText: PropTypes.string, + editDisabled: PropTypes.bool }; FeatureSelectionUIDropdown.defaultProps = { onSelectMode: () => {}, diff --git a/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionUIGeometryChips.js b/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionUIGeometryChips.js index 19a205181..bea4ebc3e 100644 --- a/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionUIGeometryChips.js +++ b/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionUIGeometryChips.js @@ -1,16 +1,18 @@ +import { Cancel } from '@mui/icons-material'; import { Box, Chip, List, ListItem, Tooltip, styled } from '@mui/material'; import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useState } from 'react'; -const ChipList = styled(List)(({ theme: { spacing } }) => ({ +const ChipList = styled(List)(({ theme }) => ({ display: 'flex', alignItems: 'center', - margin: 0, - padding: spacing(0, 1), + marginLeft: theme.spacing(1.5), + padding: 0, overflowX: 'auto', maxWidth: '100%', scrollbarWidth: 'none', msOverflowStyle: 'none', + '&::-webkit-scrollbar': { display: 'none' } @@ -26,8 +28,9 @@ const NOOP = () => {}; * @param {function} [props.onDeleteGeometry] * @param {string} [props.chipTooltip] * @param {string} [props.disabledChipTooltip] - * @param { "medium" | "small" } [props.size] + * @param { "medium" | "small" | undefined } [props.size] * @param { "bottom" | "left" | "right" | "top" | undefined } [props.tooltipPlacement] + * @param {string} [props.chipLabel] * @returns */ function FeatureSelectionUIGeometryChips({ @@ -37,7 +40,8 @@ function FeatureSelectionUIGeometryChips({ chipTooltip, disabledChipTooltip, size = 'medium', - tooltipPlacement = 'bottom' + tooltipPlacement = 'bottom', + chipLabel }) { /** * @param {GeoJSON.Geometry['type']} type @@ -52,19 +56,27 @@ function FeatureSelectionUIGeometryChips({ function getFeatureChipLabel(feature, index) { const type = translateType(feature.geometry.type); - return feature.properties?.name || `${type} ${index + 1}`; + return chipLabel || feature.properties?.name || `${type} ${index + 1}`; } + const [showDeleteTooltip, setShowDeleteTooltip] = useState(false); + return ( {features.map((geometry, index) => { const isDisabled = geometry.properties?.disabled; + const tooltipText = isDisabled + ? disabledChipTooltip || chipTooltip + : showDeleteTooltip + ? 'Remove' + : chipTooltip; + return ( onDeleteGeometry(geometry) : undefined } + deleteIcon={ + setShowDeleteTooltip(true)} + onMouseLeave={() => setShowDeleteTooltip(false)} + /> + } /> @@ -105,7 +123,8 @@ FeatureSelectionUIGeometryChips.propTypes = { 'right-start', 'top-end', 'top-start' - ]) + ]), + chipLabel: PropTypes.string }; FeatureSelectionUIGeometryChips.defaultProps = { size: 'medium', diff --git a/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionWidgetUI.js b/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionWidgetUI.js index 012a19d1c..b9ad85182 100644 --- a/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionWidgetUI.js +++ b/packages/react-ui/src/widgets/FeatureSelectionWidgetUI/FeatureSelectionWidgetUI.js @@ -5,11 +5,12 @@ import FeatureSelectionUIToggleButton from './FeatureSelectionUIToggleButton'; import FeatureSelectionUIGeometryChips from './FeatureSelectionUIGeometryChips'; import FeatureSelectionUIDropdown from './FeatureSelectionUIDropdown'; -const StylesWrapper = styled(Paper)(({ theme: { spacing, palette, shape } }) => ({ +const StylesWrapper = styled(Paper)(({ theme }) => ({ display: 'flex', alignItems: 'center', justifyContent: 'space-between', - padding: spacing(0.5) + maxHeight: theme.spacing(5), + padding: theme.spacing(0.5) })); /** @@ -33,6 +34,9 @@ const StylesWrapper = styled(Paper)(({ theme: { spacing, palette, shape } }) => * @param {function} [props.onSelectGeometry] * @param {function} [props.onDeleteGeometry] * @param { "bottom" | "left" | "right" | "top" | undefined } [props.tooltipPlacement] + * @param { "small" | "medium" | undefined } [props.size] + * @param {string} [props.chipLabel] + * * --> */ function FeatureSelectionWidgetUI({ @@ -45,7 +49,9 @@ function FeatureSelectionWidgetUI({ geometry, onSelectGeometry, onDeleteGeometry, - tooltipPlacement = 'bottom' + tooltipPlacement = 'bottom', + size = 'medium', + chipLabel }) { const selectedModeData = useMemo(() => { const modes = [ @@ -84,15 +90,18 @@ function FeatureSelectionWidgetUI({ tooltipPlacement={tooltipPlacement} tooltipText='Select a mode' menuHeaderText='Choose a selection mode' + editDisabled={!geometry} /> {!!geometry && ( )} @@ -102,7 +111,8 @@ function FeatureSelectionWidgetUI({ FeatureSelectionWidgetUI.defaultProps = { enabled: false, tooltipPlacement: 'bottom', - editModes: [] + editModes: [], + size: 'medium' }; const MODE_SHAPE = PropTypes.shape({ @@ -120,7 +130,9 @@ FeatureSelectionWidgetUI.propTypes = { onEnabledChange: PropTypes.func, geometry: PropTypes.any, onSelectGeometry: PropTypes.func, - tooltipPlacement: PropTypes.string + tooltipPlacement: PropTypes.string, + size: PropTypes.oneOf(['small', 'medium']), + chipLabel: PropTypes.string }; export default FeatureSelectionWidgetUI; diff --git a/packages/react-ui/storybook/stories/widgetsUI/FeatureSelectionWidgetUI.stories.js b/packages/react-ui/storybook/stories/widgetsUI/FeatureSelectionWidgetUI.stories.js index 4fa9033ad..144d80c3b 100644 --- a/packages/react-ui/storybook/stories/widgetsUI/FeatureSelectionWidgetUI.stories.js +++ b/packages/react-ui/storybook/stories/widgetsUI/FeatureSelectionWidgetUI.stories.js @@ -11,6 +11,12 @@ const options = { argTypes: { enabled: { control: { type: 'boolean' } + }, + size: { + control: { + type: 'select', + options: ['small', 'medium'] + } } }, parameters: { @@ -38,7 +44,7 @@ const Template = (args) => { const [selectedMode, setSelectedMode] = useState(FEATURE_SELECTION_MODES[0].id); return ( - + console.log('onDeleteGeometry') }; WithGeometry.args = WithGeometryProps; + +export const SmallSize = Template.bind({}); +const SmallSizeProps = { + geometry: { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [0.5, 0.5] + }, + properties: { + name: 'Mask' + } + }, + size: 'small', + onSelectGeometry: () => console.log('onSelectGeometry'), + onDeleteGeometry: () => console.log('onDeleteGeometry') +}; +SmallSize.args = SmallSizeProps; diff --git a/packages/react-widgets/src/widgets/FeatureSelectionWidget.js b/packages/react-widgets/src/widgets/FeatureSelectionWidget.js index 10c3afb29..d336624f5 100644 --- a/packages/react-widgets/src/widgets/FeatureSelectionWidget.js +++ b/packages/react-widgets/src/widgets/FeatureSelectionWidget.js @@ -45,7 +45,9 @@ const EDIT_MODES_MAP = { function FeatureSelectionWidget({ selectionModes: selectionModesKeys, editModes: editModesKeys, - tooltipPlacement + tooltipPlacement, + size, + chipLabel }) { const dispatch = useDispatch(); const geometry = useSelector((state) => state.carto.spatialFilter); @@ -104,6 +106,8 @@ function FeatureSelectionWidget({ geometry={geometry} onSelectGeometry={handleSelectGeometry} onDeleteGeometry={handleDeleteGeometry} + size={size} + chipLabel={chipLabel} /> ); } @@ -111,7 +115,8 @@ function FeatureSelectionWidget({ FeatureSelectionWidget.defaultProps = { selectionModes: Object.values(FEATURE_SELECTION_MODES), editModes: Object.values(EDIT_MODES_KEYS), - tooltipPlacement: FeatureSelectionWidgetUI.defaultProps.tooltipPlacement + tooltipPlacement: FeatureSelectionWidgetUI.defaultProps.tooltipPlacement, + size: FeatureSelectionWidgetUI.defaultProps.size }; FeatureSelectionWidget.propTypes = { @@ -119,7 +124,9 @@ FeatureSelectionWidget.propTypes = { PropTypes.oneOf(Object.values(FEATURE_SELECTION_MODES)) ), editModes: PropTypes.arrayOf(PropTypes.oneOf(Object.values(EDIT_MODES_KEYS))), - tooltipPlacement: FeatureSelectionWidgetUI.propTypes.tooltipPlacement + tooltipPlacement: FeatureSelectionWidgetUI.propTypes.tooltipPlacement, + size: FeatureSelectionWidgetUI.propTypes.size, + chipLabel: FeatureSelectionWidgetUI.propTypes.chipLabel }; export default FeatureSelectionWidget;