mirror of
https://github.com/strapi/strapi.git
synced 2025-11-11 07:39:16 +00:00
feat: add keyboard navigation for relations reordering
This commit is contained in:
parent
a60f265940
commit
09d8c286f4
@ -2,6 +2,7 @@ import React, { useRef, useState, useMemo, useEffect } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { FixedSizeList as List } from 'react-window';
|
import { FixedSizeList as List } from 'react-window';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { ReactSelect } from '@strapi/helper-plugin';
|
import { ReactSelect } from '@strapi/helper-plugin';
|
||||||
import { Status } from '@strapi/design-system/Status';
|
import { Status } from '@strapi/design-system/Status';
|
||||||
@ -12,6 +13,7 @@ import { FieldLabel, FieldError, FieldHint, Field } from '@strapi/design-system/
|
|||||||
import { TextButton } from '@strapi/design-system/TextButton';
|
import { TextButton } from '@strapi/design-system/TextButton';
|
||||||
import { Typography } from '@strapi/design-system/Typography';
|
import { Typography } from '@strapi/design-system/Typography';
|
||||||
import { Tooltip } from '@strapi/design-system/Tooltip';
|
import { Tooltip } from '@strapi/design-system/Tooltip';
|
||||||
|
import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
|
||||||
|
|
||||||
import Cross from '@strapi/icons/Cross';
|
import Cross from '@strapi/icons/Cross';
|
||||||
import Refresh from '@strapi/icons/Refresh';
|
import Refresh from '@strapi/icons/Refresh';
|
||||||
@ -22,6 +24,8 @@ import { RelationList } from './components/RelationList';
|
|||||||
import { Option } from './components/Option';
|
import { Option } from './components/Option';
|
||||||
import { RELATION_GUTTER, RELATION_ITEM_HEIGHT } from './constants';
|
import { RELATION_GUTTER, RELATION_ITEM_HEIGHT } from './constants';
|
||||||
|
|
||||||
|
import { getTrad } from '../../utils';
|
||||||
|
|
||||||
const LinkEllipsis = styled(Link)`
|
const LinkEllipsis = styled(Link)`
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -76,9 +80,13 @@ const RelationInput = ({
|
|||||||
size,
|
size,
|
||||||
}) => {
|
}) => {
|
||||||
const [value, setValue] = useState(null);
|
const [value, setValue] = useState(null);
|
||||||
|
const [overflow, setOverflow] = useState('');
|
||||||
|
const [liveText, setLiveText] = useState('');
|
||||||
|
|
||||||
const listRef = useRef();
|
const listRef = useRef();
|
||||||
const outerListRef = useRef();
|
const outerListRef = useRef();
|
||||||
const [overflow, setOverflow] = useState('');
|
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
const { data } = searchResults;
|
const { data } = searchResults;
|
||||||
|
|
||||||
@ -213,12 +221,68 @@ const RelationInput = ({
|
|||||||
onSearch();
|
onSearch();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} newIndex
|
||||||
|
* @param {number} currentIndex
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
const handleUpdatePositionOfRelation = (newIndex, currentIndex) => {
|
const handleUpdatePositionOfRelation = (newIndex, currentIndex) => {
|
||||||
if (onRelationReorder) {
|
if (onRelationReorder && newIndex >= 0 && newIndex < relations.length) {
|
||||||
onRelationReorder(currentIndex, newIndex);
|
onRelationReorder(currentIndex, newIndex);
|
||||||
|
|
||||||
|
const item = relations[currentIndex];
|
||||||
|
setLiveText(`${item.mainField ?? item.id}. New position in list: ${getItemPos(newIndex)}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} index
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
const getItemPos = (index) => `${index + 1} of ${relations.length}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} index
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const handleGrabItem = (index) => {
|
||||||
|
const item = relations[index];
|
||||||
|
|
||||||
|
setLiveText(
|
||||||
|
`${item.mainField ?? item.id}, grabbed. Current position in list: ${getItemPos(
|
||||||
|
index
|
||||||
|
)}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} index
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const handleDropItem = (index) => {
|
||||||
|
const item = relations[index];
|
||||||
|
|
||||||
|
setLiveText(
|
||||||
|
`${item.mainField ?? item.id}, dropped. Final position in list: ${getItemPos(index)}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} index
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const handleCancel = (index) => {
|
||||||
|
const item = relations[index];
|
||||||
|
|
||||||
|
setLiveText(`${item.mainField ?? item.id}, dropped. Re-order cancelled.`);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field error={error} name={name} hint={description} id={id}>
|
<Field error={error} name={name} hint={description} id={id}>
|
||||||
<Relation
|
<Relation
|
||||||
@ -287,6 +351,13 @@ const RelationInput = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<RelationList overflow={overflow}>
|
<RelationList overflow={overflow}>
|
||||||
|
<VisuallyHidden id={`${name}-item-instructions`}>
|
||||||
|
{formatMessage({
|
||||||
|
id: getTrad('components.RelationInput.instructions'),
|
||||||
|
defaultMessage: `Press spacebar to grab and re-order`,
|
||||||
|
})}
|
||||||
|
</VisuallyHidden>
|
||||||
|
<VisuallyHidden aria-live="assertive">{liveText}</VisuallyHidden>
|
||||||
<List
|
<List
|
||||||
height={dynamicListHeight}
|
height={dynamicListHeight}
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
@ -294,7 +365,11 @@ const RelationInput = ({
|
|||||||
itemCount={totalNumberOfRelations}
|
itemCount={totalNumberOfRelations}
|
||||||
itemSize={RELATION_ITEM_HEIGHT + RELATION_GUTTER}
|
itemSize={RELATION_ITEM_HEIGHT + RELATION_GUTTER}
|
||||||
itemData={{
|
itemData={{
|
||||||
|
ariaDescribedBy: `${name}-item-instructions`,
|
||||||
disabled,
|
disabled,
|
||||||
|
handleCancel,
|
||||||
|
handleDropItem,
|
||||||
|
handleGrabItem,
|
||||||
labelDisconnectRelation,
|
labelDisconnectRelation,
|
||||||
onRelationDisconnect,
|
onRelationDisconnect,
|
||||||
publicationStateTranslations,
|
publicationStateTranslations,
|
||||||
@ -395,7 +470,11 @@ RelationInput.propTypes = {
|
|||||||
*/
|
*/
|
||||||
const ListItem = ({ data, index, style }) => {
|
const ListItem = ({ data, index, style }) => {
|
||||||
const {
|
const {
|
||||||
|
ariaDescribedBy,
|
||||||
disabled,
|
disabled,
|
||||||
|
handleCancel,
|
||||||
|
handleDropItem,
|
||||||
|
handleGrabItem,
|
||||||
labelDisconnectRelation,
|
labelDisconnectRelation,
|
||||||
onRelationDisconnect,
|
onRelationDisconnect,
|
||||||
publicationStateTranslations,
|
publicationStateTranslations,
|
||||||
@ -409,11 +488,11 @@ const ListItem = ({ data, index, style }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RelationItem
|
<RelationItem
|
||||||
disabled={disabled}
|
ariaDescribedBy={ariaDescribedBy}
|
||||||
canDrag={canDrag}
|
canDrag={canDrag}
|
||||||
|
disabled={disabled}
|
||||||
id={id}
|
id={id}
|
||||||
index={index}
|
index={index}
|
||||||
updatePositionOfRelation={updatePositionOfRelation}
|
|
||||||
endAction={
|
endAction={
|
||||||
<DisconnectButton
|
<DisconnectButton
|
||||||
data-testid={`remove-relation-${id}`}
|
data-testid={`remove-relation-${id}`}
|
||||||
@ -425,11 +504,15 @@ const ListItem = ({ data, index, style }) => {
|
|||||||
<Icon width="12px" as={Cross} />
|
<Icon width="12px" as={Cross} />
|
||||||
</DisconnectButton>
|
</DisconnectButton>
|
||||||
}
|
}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onDrop={handleDropItem}
|
||||||
|
onGrab={handleGrabItem}
|
||||||
style={{
|
style={{
|
||||||
...style,
|
...style,
|
||||||
bottom: style.bottom ?? 0 + RELATION_GUTTER,
|
bottom: style.bottom ?? 0 + RELATION_GUTTER,
|
||||||
height: style.height ?? 0 - RELATION_GUTTER,
|
height: style.height ?? 0 - RELATION_GUTTER,
|
||||||
}}
|
}}
|
||||||
|
updatePositionOfRelation={updatePositionOfRelation}
|
||||||
>
|
>
|
||||||
<BoxEllipsis minWidth={0} paddingTop={1} paddingBottom={1} paddingRight={4}>
|
<BoxEllipsis minWidth={0} paddingTop={1} paddingBottom={1} paddingRight={4}>
|
||||||
<Tooltip description={mainField ?? `${id}`}>
|
<Tooltip description={mainField ?? `${id}`}>
|
||||||
@ -462,7 +545,11 @@ ListItem.defaultProps = {
|
|||||||
|
|
||||||
ListItem.propTypes = {
|
ListItem.propTypes = {
|
||||||
data: PropTypes.shape({
|
data: PropTypes.shape({
|
||||||
|
ariaDescribedBy: PropTypes.string.isRequired,
|
||||||
disabled: PropTypes.bool.isRequired,
|
disabled: PropTypes.bool.isRequired,
|
||||||
|
handleCancel: PropTypes.func,
|
||||||
|
handleDropItem: PropTypes.func,
|
||||||
|
handleGrabItem: PropTypes.func,
|
||||||
labelDisconnectRelation: PropTypes.string.isRequired,
|
labelDisconnectRelation: PropTypes.string.isRequired,
|
||||||
onRelationDisconnect: PropTypes.func.isRequired,
|
onRelationDisconnect: PropTypes.func.isRequired,
|
||||||
publicationStateTranslations: PropTypes.shape({
|
publicationStateTranslations: PropTypes.shape({
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useDrag, useDrop } from 'react-dnd';
|
import { useDrag, useDrop } from 'react-dnd';
|
||||||
@ -10,6 +10,7 @@ import { IconButton } from '@strapi/design-system/IconButton';
|
|||||||
import Drag from '@strapi/icons/Drag';
|
import Drag from '@strapi/icons/Drag';
|
||||||
|
|
||||||
import { composeRefs } from '../../../utils';
|
import { composeRefs } from '../../../utils';
|
||||||
|
import { RELATION_GUTTER } from '../constants';
|
||||||
|
|
||||||
const ChildrenWrapper = styled(Flex)`
|
const ChildrenWrapper = styled(Flex)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -20,6 +21,7 @@ const ChildrenWrapper = styled(Flex)`
|
|||||||
const RELATION_ITEM_DRAG_TYPE = 'RelationItem';
|
const RELATION_ITEM_DRAG_TYPE = 'RelationItem';
|
||||||
|
|
||||||
export const RelationItem = ({
|
export const RelationItem = ({
|
||||||
|
ariaDescribedBy,
|
||||||
children,
|
children,
|
||||||
canDrag,
|
canDrag,
|
||||||
disabled,
|
disabled,
|
||||||
@ -27,9 +29,13 @@ export const RelationItem = ({
|
|||||||
style,
|
style,
|
||||||
id,
|
id,
|
||||||
index,
|
index,
|
||||||
|
onCancel,
|
||||||
|
onDropItem,
|
||||||
|
onGrabItem,
|
||||||
updatePositionOfRelation,
|
updatePositionOfRelation,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
|
const [isSelected, setIsSelected] = useState(false);
|
||||||
const relationRef = useRef(null);
|
const relationRef = useRef(null);
|
||||||
|
|
||||||
const [{ handlerId }, dropRef] = useDrop({
|
const [{ handlerId }, dropRef] = useDrop({
|
||||||
@ -85,8 +91,84 @@ export const RelationItem = ({
|
|||||||
|
|
||||||
const composedRefs = composeRefs(relationRef, dragRef);
|
const composedRefs = composeRefs(relationRef, dragRef);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {(movement: 'UP' | 'DOWN') => void})}
|
||||||
|
*/
|
||||||
|
const handleMove = (movement) => {
|
||||||
|
if (!isSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (movement === 'UP') {
|
||||||
|
updatePositionOfRelation(index - 1, index);
|
||||||
|
} else if (movement === 'DOWN') {
|
||||||
|
updatePositionOfRelation(index + 1, index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragClick = () => {
|
||||||
|
if (isSelected) {
|
||||||
|
if (onDropItem) {
|
||||||
|
onDropItem(index);
|
||||||
|
}
|
||||||
|
setIsSelected(false);
|
||||||
|
} else {
|
||||||
|
if (onGrabItem) {
|
||||||
|
onGrabItem(index);
|
||||||
|
}
|
||||||
|
setIsSelected(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsSelected(false);
|
||||||
|
|
||||||
|
if (onCancel) {
|
||||||
|
onCancel(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {React.KeyboardEventHandler<HTMLButtonElement>}
|
||||||
|
*/
|
||||||
|
const handleKeyDown = (e) => {
|
||||||
|
if (e.key === 'Tab' && !isSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
switch (e.key) {
|
||||||
|
case ' ':
|
||||||
|
handleDragClick();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Escape':
|
||||||
|
handleCancel();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowDown':
|
||||||
|
case 'ArrowRight':
|
||||||
|
handleMove('DOWN');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowUp':
|
||||||
|
case 'ArrowLeft':
|
||||||
|
handleMove('UP');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box style={style} as="li" ref={dropRef}>
|
<Box
|
||||||
|
style={style}
|
||||||
|
as="li"
|
||||||
|
ref={dropRef}
|
||||||
|
aria-describedby={ariaDescribedBy}
|
||||||
|
cursor={canDrag ? 'all-scroll' : 'default'}
|
||||||
|
>
|
||||||
{isDragging ? (
|
{isDragging ? (
|
||||||
<Box
|
<Box
|
||||||
ref={dragPreviewRef}
|
ref={dragPreviewRef}
|
||||||
@ -99,7 +181,7 @@ export const RelationItem = ({
|
|||||||
borderColor="primary600"
|
borderColor="primary600"
|
||||||
borderWidth="1px"
|
borderWidth="1px"
|
||||||
background="primary100"
|
background="primary100"
|
||||||
height="100%"
|
height={`calc(100% - ${RELATION_GUTTER}px)`}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Flex
|
<Flex
|
||||||
@ -118,7 +200,14 @@ export const RelationItem = ({
|
|||||||
>
|
>
|
||||||
{/* TODO: swap this out for using children when DS is updated */}
|
{/* TODO: swap this out for using children when DS is updated */}
|
||||||
{canDrag ? (
|
{canDrag ? (
|
||||||
<IconButton marginRight={1} aria-label="Drag" noBorder icon={<Drag />} />
|
<IconButton
|
||||||
|
marginRight={1}
|
||||||
|
aria-label="Drag"
|
||||||
|
noBorder
|
||||||
|
icon={<Drag />}
|
||||||
|
onClick={handleDragClick}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<ChildrenWrapper justifyContent="space-between">{children}</ChildrenWrapper>
|
<ChildrenWrapper justifyContent="space-between">{children}</ChildrenWrapper>
|
||||||
{endAction && <Box paddingLeft={4}>{endAction}</Box>}
|
{endAction && <Box paddingLeft={4}>{endAction}</Box>}
|
||||||
@ -129,20 +218,28 @@ export const RelationItem = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
RelationItem.defaultProps = {
|
RelationItem.defaultProps = {
|
||||||
|
ariaDescribedBy: '',
|
||||||
canDrag: false,
|
canDrag: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
endAction: undefined,
|
endAction: undefined,
|
||||||
|
onCancel: undefined,
|
||||||
|
onDropItem: undefined,
|
||||||
|
onGrabItem: undefined,
|
||||||
style: undefined,
|
style: undefined,
|
||||||
updatePositionOfRelation: undefined,
|
updatePositionOfRelation: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
RelationItem.propTypes = {
|
RelationItem.propTypes = {
|
||||||
|
ariaDescribedBy: PropTypes.string,
|
||||||
canDrag: PropTypes.bool,
|
canDrag: PropTypes.bool,
|
||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
endAction: PropTypes.node,
|
endAction: PropTypes.node,
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
index: PropTypes.number.isRequired,
|
index: PropTypes.number.isRequired,
|
||||||
|
onCancel: PropTypes.func,
|
||||||
|
onDropItem: PropTypes.func,
|
||||||
|
onGrabItem: PropTypes.func,
|
||||||
style: PropTypes.shape({
|
style: PropTypes.shape({
|
||||||
height: PropTypes.number,
|
height: PropTypes.number,
|
||||||
left: PropTypes.number,
|
left: PropTypes.number,
|
||||||
|
|||||||
@ -1,18 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||||
import { DndProvider } from 'react-dnd';
|
import { DndProvider } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
|
|
||||||
import { RelationItem } from '../RelationItem';
|
import { RelationItem } from '../RelationItem';
|
||||||
|
|
||||||
const setup = ({ endAction }) =>
|
const setup = ({ endAction, testingDnd = false, ...props }) =>
|
||||||
render(
|
render(
|
||||||
<ThemeProvider theme={lightTheme}>
|
<ThemeProvider theme={lightTheme}>
|
||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
<RelationItem id={0} index={0} endAction={endAction}>
|
<RelationItem canDrag={testingDnd} id={0} index={0} endAction={endAction} {...props}>
|
||||||
First relation
|
First relation
|
||||||
</RelationItem>
|
</RelationItem>
|
||||||
|
{testingDnd ? (
|
||||||
|
<RelationItem canDrag={testingDnd} id={1} index={1} endAction={endAction} {...props}>
|
||||||
|
Second relation
|
||||||
|
</RelationItem>
|
||||||
|
) : null}
|
||||||
</DndProvider>
|
</DndProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
@ -29,4 +34,58 @@ describe('Content-Manager || RelationInput || RelationItem', () => {
|
|||||||
|
|
||||||
expect(getByText('end action here')).toBeInTheDocument();
|
expect(getByText('end action here')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Reordering relations', () => {
|
||||||
|
it('should not move with arrow keys if the button is not pressed first', () => {
|
||||||
|
const updatePositionOfRelationMock = jest.fn();
|
||||||
|
|
||||||
|
setup({ updatePositionOfRelation: updatePositionOfRelationMock, testingDnd: true });
|
||||||
|
|
||||||
|
const [draggedItem] = screen.getAllByLabelText('Drag');
|
||||||
|
|
||||||
|
fireEvent.keyDown(draggedItem, { key: 'ArrowDown', code: 'ArrowDown' });
|
||||||
|
|
||||||
|
expect(updatePositionOfRelationMock).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should move with the arrow keys if the button has been activated first', () => {
|
||||||
|
const updatePositionOfRelationMock = jest.fn();
|
||||||
|
|
||||||
|
setup({ updatePositionOfRelation: updatePositionOfRelationMock, testingDnd: true });
|
||||||
|
|
||||||
|
const [draggedItem] = screen.getAllByLabelText('Drag');
|
||||||
|
|
||||||
|
fireEvent.keyDown(draggedItem, { key: ' ', code: 'Space' });
|
||||||
|
fireEvent.keyDown(draggedItem, { key: 'ArrowDown', code: 'ArrowDown' });
|
||||||
|
|
||||||
|
expect(updatePositionOfRelationMock).toBeCalledWith(1, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not fire reorderRelation if the item is trying to go up and is the first item', () => {
|
||||||
|
const updatePositionOfRelationMock = jest.fn();
|
||||||
|
|
||||||
|
setup({ updatePositionOfRelation: updatePositionOfRelationMock, testingDnd: true });
|
||||||
|
|
||||||
|
const [draggedItem] = screen.getAllByLabelText('Drag');
|
||||||
|
|
||||||
|
fireEvent.keyDown(draggedItem, { key: ' ', code: 'Space' });
|
||||||
|
fireEvent.keyDown(draggedItem, { key: 'ArrowUp', code: 'ArrowUp' });
|
||||||
|
|
||||||
|
expect(updatePositionOfRelationMock).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should exit drag and drop mode when the escape key is pressed', () => {
|
||||||
|
const updatePositionOfRelationMock = jest.fn();
|
||||||
|
|
||||||
|
setup({ updatePositionOfRelation: updatePositionOfRelationMock, testingDnd: true });
|
||||||
|
|
||||||
|
const [draggedItem] = screen.getAllByLabelText('Drag');
|
||||||
|
|
||||||
|
fireEvent.keyDown(draggedItem, { key: ' ', code: 'Space' });
|
||||||
|
fireEvent.keyDown(draggedItem, { key: 'Escape', code: 'Escape' });
|
||||||
|
fireEvent.keyDown(draggedItem, { key: 'ArrowUp', code: 'ArrowUp' });
|
||||||
|
|
||||||
|
expect(updatePositionOfRelationMock).not.toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -389,7 +389,7 @@ describe('RelationInputDataManager', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Reorder an entity', () => {
|
test('reordering an entity', () => {
|
||||||
const { reorderRelation } = useCMEditViewDataManager();
|
const { reorderRelation } = useCMEditViewDataManager();
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
|
|||||||
@ -554,6 +554,7 @@
|
|||||||
"content-manager.components.LeftMenu.single-types": "Single Types",
|
"content-manager.components.LeftMenu.single-types": "Single Types",
|
||||||
"content-manager.components.LimitSelect.itemsPerPage": "Items per page",
|
"content-manager.components.LimitSelect.itemsPerPage": "Items per page",
|
||||||
"content-manager.components.NotAllowedInput.text": "No permissions to see this field",
|
"content-manager.components.NotAllowedInput.text": "No permissions to see this field",
|
||||||
|
"content-manager.components.RelationInput.instructions": "Press spacebar to grab and re-order",
|
||||||
"content-manager.components.RepeatableComponent.error-message": "The component(s) contain error(s)",
|
"content-manager.components.RepeatableComponent.error-message": "The component(s) contain error(s)",
|
||||||
"content-manager.components.Search.placeholder": "Search for an entry...",
|
"content-manager.components.Search.placeholder": "Search for an entry...",
|
||||||
"content-manager.components.Select.draft-info-title": "State: Draft",
|
"content-manager.components.Select.draft-info-title": "State: Draft",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user