diff --git a/change/@fluentui-react-examples-2020-10-16-11-19-05-uppdroptarget.json b/change/@fluentui-react-examples-2020-10-16-11-19-05-uppdroptarget.json new file mode 100644 index 0000000000000..d7827a008424b --- /dev/null +++ b/change/@fluentui-react-examples-2020-10-16-11-19-05-uppdroptarget.json @@ -0,0 +1,8 @@ +{ + "type": "none", + "comment": "Updating the UPP examples to have different drag drop abilities", + "packageName": "@fluentui/react-examples", + "email": "elvonspa@microsoft.com", + "dependentChangeType": "none", + "date": "2020-10-16T18:19:05.441Z" +} diff --git a/change/@fluentui-react-experiments-2020-10-19-11-03-03-uppdroptarget.json b/change/@fluentui-react-experiments-2020-10-19-11-03-03-uppdroptarget.json new file mode 100644 index 0000000000000..8992caab7d8a0 --- /dev/null +++ b/change/@fluentui-react-experiments-2020-10-19-11-03-03-uppdroptarget.json @@ -0,0 +1,8 @@ +{ + "type": "minor", + "comment": "Add props to UPP to disable drag drop, drop to end of list", + "packageName": "@fluentui/react-experiments", + "email": "elvonspa@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-10-19T18:03:03.534Z" +} diff --git a/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/DoubleUnifiedPeoplePicker.Example.tsx b/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/DoubleUnifiedPeoplePicker.Example.tsx index 2bcf549b953fc..0df5780b4bb30 100644 --- a/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/DoubleUnifiedPeoplePicker.Example.tsx +++ b/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/DoubleUnifiedPeoplePicker.Example.tsx @@ -162,10 +162,19 @@ const UnifiedPeoplePickerExample = (): JSX.Element => { updatedItems.push(item); } } + if (insertIndex === currentItems.length) { + newItems.forEach(draggedItem => { + updatedItems.push(draggedItem); + }); + } setPeopleSelectedItems(updatedItems); } }; + const _itemsAreEqual = (item1?: any, item2?: any): boolean => { + return item1?.key === item2?.key; + }; + const _onItemsRemoved = (itemsToRemove: IPersonaProps[]): void => { // Updating the local copy as well at the parent level. const currentItems: IPersonaProps[] = [...peopleSelectedItems]; @@ -213,6 +222,7 @@ const UnifiedPeoplePickerExample = (): JSX.Element => { serializeItemsForDrag: _serializeItemsForDrag, deserializeItemsFromDrop: _deserializeItemsFromDrop, dropItemsAt: _dropItemsAt, + itemsAreEqual: _itemsAreEqual, } as ISelectedPeopleListProps; const inputProps = { diff --git a/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/UnifiedPeoplePicker.Example.tsx b/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/UnifiedPeoplePicker.Example.tsx index 936b8e8005071..5c5347d43a032 100644 --- a/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/UnifiedPeoplePicker.Example.tsx +++ b/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/UnifiedPeoplePicker.Example.tsx @@ -219,6 +219,7 @@ export const UnifiedPeoplePickerExample = (): JSX.Element => { onInputChange={_onInputChange} // eslint-disable-next-line react/jsx-no-bind onPaste={_onPaste} + autofillDragDropEnabled={false} /> ); diff --git a/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/UnifiedPeoplePicker.WithEdit.Example.tsx b/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/UnifiedPeoplePicker.WithEdit.Example.tsx index 59d74c368e867..cc20c882bd675 100644 --- a/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/UnifiedPeoplePicker.WithEdit.Example.tsx +++ b/packages/react-examples/src/react-experiments/UnifiedPeoplePicker/UnifiedPeoplePicker.WithEdit.Example.tsx @@ -154,29 +154,6 @@ export const UnifiedPeoplePickerWithEditExample = (): JSX.Element => { setPeopleSelectedItems(prevPeopleSelectedItems => [...prevPeopleSelectedItems, ...newList]); }; - const _dropItemsAt = (insertIndex: number, newItems: IPersonaProps[], indicesToRemove: number[]): void => { - // Insert those items into the current list - if (insertIndex > -1) { - const currentItems: IPersonaProps[] = [...peopleSelectedItems]; - const updatedItems: IPersonaProps[] = []; - - for (let i = 0; i < currentItems.length; i++) { - const item = currentItems[i]; - // If this is the insert before index, insert the dragged items, then the current item - if (i === insertIndex) { - newItems.forEach(draggedItem => { - updatedItems.push(draggedItem); - }); - updatedItems.push(item); - } else if (!indicesToRemove.includes(i)) { - // only insert items into the new list that are not being dragged - updatedItems.push(item); - } - } - setPeopleSelectedItems(updatedItems); - } - }; - const _onItemsRemoved = (itemsToRemove: IPersonaProps[]): void => { // Updating the local copy as well at the parent level. const currentItems: IPersonaProps[] = [...peopleSelectedItems]; @@ -241,7 +218,6 @@ export const UnifiedPeoplePickerWithEditExample = (): JSX.Element => { removeButtonAriaLabel: 'Remove', onItemsRemoved: _onItemsRemoved, getItemCopyText: _getItemsCopyText, - dropItemsAt: _dropItemsAt, onRenderItem: SelectedItem, replaceItem: _replaceItem, } as ISelectedPeopleListProps; @@ -261,6 +237,7 @@ export const UnifiedPeoplePickerWithEditExample = (): JSX.Element => { onInputChange={_onInputChange} // eslint-disable-next-line react/jsx-no-bind onPaste={_onPaste} + defaultDragDropEnabled={false} /> ); diff --git a/packages/react-experiments/src/components/SelectedItemsList/SelectedItemsList.types.ts b/packages/react-experiments/src/components/SelectedItemsList/SelectedItemsList.types.ts index 8f1f098b69632..917cbea242504 100644 --- a/packages/react-experiments/src/components/SelectedItemsList/SelectedItemsList.types.ts +++ b/packages/react-experiments/src/components/SelectedItemsList/SelectedItemsList.types.ts @@ -134,4 +134,10 @@ export interface ISelectedItemsListProps extends React.ClassAttributes { * Callback for when an item needs to be replaced with another item or items */ replaceItem?: (newItem: T | T[], index: number) => void; + + /** + * Callback to check to see if two items are equal + * Should be used if it's possible to change some properties on items so a strict compare will fail + */ + itemsAreEqual?: (item1?: any, item2?: any) => boolean; } diff --git a/packages/react-experiments/src/components/SelectedItemsList/SelectedPeopleList/Items/SelectedPersona.tsx b/packages/react-experiments/src/components/SelectedItemsList/SelectedPeopleList/Items/SelectedPersona.tsx index b7fa63e3521a5..8199a851ea294 100644 --- a/packages/react-experiments/src/components/SelectedItemsList/SelectedPeopleList/Items/SelectedPersona.tsx +++ b/packages/react-experiments/src/components/SelectedItemsList/SelectedPeopleList/Items/SelectedPersona.tsx @@ -97,7 +97,7 @@ const SelectedPersonaInner = React.memo( ); const isDraggable = React.useMemo( - () => (dragDropEvents ? !!(dragDropEvents.canDrag && dragDropEvents.canDrop) : undefined), + () => (dragDropEvents && dragDropEvents.canDrag ? !!dragDropEvents.canDrag!() : undefined), [dragDropEvents], ); diff --git a/packages/react-experiments/src/components/UnifiedPicker/UnifiedPeoplePicker/__snapshots__/UnifiedPeoplePicker.test.tsx.snap b/packages/react-experiments/src/components/UnifiedPicker/UnifiedPeoplePicker/__snapshots__/UnifiedPeoplePicker.test.tsx.snap index fe595be8eba02..967213ae426b3 100644 --- a/packages/react-experiments/src/components/UnifiedPicker/UnifiedPeoplePicker/__snapshots__/UnifiedPeoplePicker.test.tsx.snap +++ b/packages/react-experiments/src/components/UnifiedPicker/UnifiedPeoplePicker/__snapshots__/UnifiedPeoplePicker.test.tsx.snap @@ -61,6 +61,8 @@ exports[`UnifiedPeoplePicker renders correctly with no items 1`] = ` display: flex; flex: 1 1 auto; } + onDragOver={[Function]} + onDrop={[Function]} role="combobox" > (props: IUnifiedPickerProps): JSX. onInputChange, } = props; + const defaultDragDropEnabled = React.useMemo( + () => (props.defaultDragDropEnabled !== undefined ? props.defaultDragDropEnabled : true), + [props.defaultDragDropEnabled], + ); + + const autofillDragDropEnabled = React.useMemo( + () => (props.autofillDragDropEnabled !== undefined ? props.autofillDragDropEnabled : defaultDragDropEnabled), + [props.autofillDragDropEnabled, defaultDragDropEnabled], + ); + React.useImperativeHandle(props.componentRef, () => ({ clearInput: () => { if (input.current) { @@ -105,16 +115,49 @@ export const UnifiedPicker = (props: IUnifiedPickerProps): JSX. insertIndex = -1; }; + const _onDragOverAutofill = (event?: React.DragEvent) => { + if (autofillDragDropEnabled) { + event?.preventDefault(); + } + }; + + const _onDropAutoFill = (event?: React.DragEvent) => { + event?.preventDefault(); + if (props.onDropAutoFill) { + props.onDropAutoFill(event); + } else { + insertIndex = selectedItems.length; + _onDropInner(event?.dataTransfer); + } + }; + const _canDrop = (dropContext?: IDragDropContext, dragContext?: IDragDropContext): boolean => { - return !focusedItemIndices.includes(dropContext!.index); + return defaultDragDropEnabled && !focusedItemIndices.includes(dropContext!.index); + }; + + const _onDropList = (item?: any, event?: DragEvent): void => { + /* indexOf compares using strict equality + if the item is something where properties can change frequently, then the + itemsAreEqual prop should be overloaded + Otherwise it's possible for the indexOf check to fail and return -1 */ + if (props.selectedItemsListProps.itemsAreEqual) { + insertIndex = selectedItems.findIndex(currentItem => + props.selectedItemsListProps.itemsAreEqual + ? props.selectedItemsListProps.itemsAreEqual(currentItem, item) + : false, + ); + } else { + insertIndex = selectedItems.indexOf(item); + } + + event?.preventDefault(); + _onDropInner(event?.dataTransfer !== null ? event?.dataTransfer : undefined); }; - const _onDrop = (item?: any, event?: DragEvent): void => { - insertIndex = selectedItems.indexOf(item); + const _onDropInner = (dataTransfer?: DataTransfer): void => { let isDropHandled = false; - if (event?.dataTransfer) { - event.preventDefault(); - const data = event.dataTransfer.items; + if (dataTransfer) { + const data = dataTransfer.items; for (let i = 0; i < data.length; i++) { if (data[i].kind === 'string' && data[i].type === props.customClipboardType) { isDropHandled = true; @@ -168,10 +211,10 @@ export const UnifiedPicker = (props: IUnifiedPickerProps): JSX. const defaultDragDropEvents: IDragDropEvents = { canDrop: _canDrop, - canDrag: () => true, + canDrag: () => defaultDragDropEnabled, onDragEnter: _onDragEnter, onDragLeave: () => undefined, - onDrop: _onDrop, + onDrop: _onDropList, onDragStart: _onDragStart, onDragEnd: _onDragEnd, }; @@ -358,6 +401,8 @@ export const UnifiedPicker = (props: IUnifiedPickerProps): JSX. aria-haspopup="listbox" role="combobox" className={css('ms-BasePicker-div', classNames.pickerDiv)} + onDrop={_onDropAutoFill} + onDragOver={_onDragOverAutofill} > { * if this is used */ customClipboardType?: string; + + /** + * If dragDropEvents is set, this property will be ignored + * @defaultvalue true + */ + defaultDragDropEnabled?: boolean; + + /** + * If this property is not specified, defaultDragDropEnabled will be used + * @defaultvalue true + */ + autofillDragDropEnabled?: boolean; + + /** + * Function to customize drop behavior over the autofill portion + * If this is not set, but autofillDragDropEnabled is, the built + * in drop behavior will be used. + */ + onDropAutoFill?: (event?: React.DragEvent) => void; } diff --git a/packages/react-experiments/src/components/UnifiedPicker/__snapshots__/UnifiedPicker.test.tsx.snap b/packages/react-experiments/src/components/UnifiedPicker/__snapshots__/UnifiedPicker.test.tsx.snap index 346ec9f0c75e4..6be0e81932ec0 100644 --- a/packages/react-experiments/src/components/UnifiedPicker/__snapshots__/UnifiedPicker.test.tsx.snap +++ b/packages/react-experiments/src/components/UnifiedPicker/__snapshots__/UnifiedPicker.test.tsx.snap @@ -61,6 +61,8 @@ exports[`UnifiedPicker renders correctly with no items 1`] = ` display: flex; flex: 1 1 auto; } + onDragOver={[Function]} + onDrop={[Function]} role="combobox" > ( updatedItems.push(item); } } + // if the insert index is at the end, add them now + if (insertIndex === currentItems.length) { + itemsToAdd.forEach(draggedItem => { + updatedItems.push(draggedItem); + }); + } setSelectedItems(updatedItems); selection.setItems(updatedItems); };