diff --git a/packages/core/admin/admin/src/content-manager/components/RelationInput/RelationInput.js b/packages/core/admin/admin/src/content-manager/components/RelationInput/RelationInput.js index fd0ed9fb0b..6e7ae59e41 100644 --- a/packages/core/admin/admin/src/content-manager/components/RelationInput/RelationInput.js +++ b/packages/core/admin/admin/src/content-manager/components/RelationInput/RelationInput.js @@ -2,7 +2,6 @@ import React, { useRef, useState, useMemo, useEffect } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { FixedSizeList as List } from 'react-window'; -import { useIntl } from 'react-intl'; import { ReactSelect } from '@strapi/helper-plugin'; import { Status } from '@strapi/design-system/Status'; @@ -24,7 +23,6 @@ import { RelationList } from './components/RelationList'; import { Option } from './components/Option'; import { RELATION_GUTTER, RELATION_ITEM_HEIGHT } from './constants'; -import { getTrad } from '../../utils'; import { usePrev } from '../../hooks'; const LinkEllipsis = styled(Link)` @@ -58,6 +56,7 @@ const RelationInput = ({ description, disabled, error, + iconButtonAriaLabel, id, name, numberOfRelationsToDisplay, @@ -65,7 +64,12 @@ const RelationInput = ({ labelAction, labelLoadMore, labelDisconnectRelation, + listAriaDescription, + liveText, loadingMessage, + onCancel, + onDropItem, + onGrabItem, noRelationsMessage, onRelationConnect, onRelationLoadMore, @@ -82,13 +86,10 @@ const RelationInput = ({ }) => { const [value, setValue] = useState(null); const [overflow, setOverflow] = useState(''); - const [liveText, setLiveText] = useState(''); const listRef = useRef(); const outerListRef = useRef(); - const { formatMessage } = useIntl(); - const { data } = searchResults; const relations = paginatedRelations.data; @@ -232,58 +233,9 @@ const RelationInput = ({ const handleUpdatePositionOfRelation = (newIndex, currentIndex) => { if (onRelationReorder && newIndex >= 0 && newIndex < relations.length) { 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.`); - }; - const previewRelationsLength = usePrev(relations.length); /** * @type {React.MutableRefObject<'onChange' | 'loadMore'>} @@ -371,12 +323,7 @@ const RelationInput = ({ } > - - {formatMessage({ - id: getTrad('components.RelationInput.instructions'), - defaultMessage: `Press spacebar to grab and re-order`, - })} - + {listAriaDescription} {liveText} { + const [liveText, setLiveText] = useState(''); + const { formatMessage } = useIntl(); const { slug, @@ -137,6 +139,12 @@ export const RelationInputDataManager = ({ const handleSearchMore = () => { search.fetchNextPage(); }; + /** + * + * @param {number} index + * @returns {string} + */ + const getItemPos = (index) => `${index + 1} of ${relationsFromModifiedData.length}`; /** * @@ -144,6 +152,20 @@ export const RelationInputDataManager = ({ * @param {number} oldIndex */ const handleRelationReorder = (oldIndex, newIndex) => { + const item = relations[oldIndex]; + setLiveText( + formatMessage( + { + id: getTrad('components.RelationInputDataManager.reorder'), + defaultMessage: '{item}, moved. New position in list: {position}.', + }, + { + item: item.mainField ?? item.id, + position: getItemPos(newIndex), + } + ) + ); + relationReorder({ name, newIndex, @@ -151,6 +173,71 @@ export const RelationInputDataManager = ({ }); }; + /** + * + * @param {number} index + * @returns {void} + */ + const handleGrabItem = (index) => { + const item = relations[index]; + + setLiveText( + formatMessage( + { + id: getTrad('components.RelationInputDataManager.grab-item'), + defaultMessage: `{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.`, + }, + { + item: item.mainField ?? item.id, + position: getItemPos(index), + } + ) + ); + }; + + /** + * + * @param {number} index + * @returns {void} + */ + const handleDropItem = (index) => { + const item = relations[index]; + + setLiveText( + formatMessage( + { + id: getTrad('components.RelationInputDataManager.drop-item'), + defaultMessage: `{item}, dropped. Final position in list: {position}.`, + }, + { + item: item.mainField ?? item.id, + position: getItemPos(index), + } + ) + ); + }; + + /** + * + * @param {number} index + * @returns {void} + */ + const handleCancel = (index) => { + const item = relations[index]; + + setLiveText( + formatMessage( + { + id: getTrad('components.RelationInputDataManager.cancel-item'), + defaultMessage: '{item}, dropped. Re-order cancelled.', + }, + { + item: item.mainField ?? item.id, + } + ) + ); + }; + if ( (!isFieldAllowed && isCreatingEntry) || (!isCreatingEntry && !isFieldAllowed && !isFieldReadable) @@ -185,25 +272,30 @@ export const RelationInputDataManager = ({ error={error} description={description} disabled={isDisabled} + iconButtonAriaLabel={formatMessage({ + id: getTrad('components.RelationInput.icon-button-aria-label'), + defaultMessage: 'Drag', + })} id={name} label={`${formatMessage({ id: intlLabel.id, defaultMessage: intlLabel.defaultMessage, })} ${totalRelations > 0 ? `(${totalRelations})` : ''}`} labelAction={labelAction} - labelLoadMore={ - !isCreatingEntry - ? formatMessage({ - id: getTrad('relation.loadMore'), - defaultMessage: 'Load More', - }) - : null - } + labelLoadMore={formatMessage({ + id: getTrad('relation.loadMore'), + defaultMessage: 'Load More', + })} labelDisconnectRelation={formatMessage({ id: getTrad('relation.disconnect'), defaultMessage: 'Remove', })} + listAriaDescription={formatMessage({ + id: getTrad('components.RelationInput.instructions'), + defaultMessage: `Press spacebar to grab and re-order`, + })} listHeight={320} + liveText={liveText} loadingMessage={formatMessage({ id: getTrad('relation.isLoading'), defaultMessage: 'Relations are loading', @@ -214,6 +306,9 @@ export const RelationInputDataManager = ({ defaultMessage: 'No relations available', })} numberOfRelationsToDisplay={RELATIONS_TO_DISPLAY} + onDropItem={handleDropItem} + onGrabItem={handleGrabItem} + onCancel={handleCancel} onRelationConnect={handleRelationConnect} onRelationDisconnect={handleRelationDisconnect} onRelationLoadMore={handleRelationLoadMore} diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json index ba3d91a723..c2228f9578 100644 --- a/packages/core/admin/admin/src/translations/en.json +++ b/packages/core/admin/admin/src/translations/en.json @@ -568,6 +568,10 @@ "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.RelationInput.icon-button-aria-label": "Drag", + "content-manager.components.RelationInputDataManager.reorder": "{item}, moved. New position in list: {position}.", + "content-manager.components.RelationInputDataManager.grab-item": "{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.", + "content-manager.components.RelationInputDataManager.drop-item": "{item}, dropped. Final position in list: {position}.", + "content-manager.components.RelationInputDataManager.cancel-item": "{item}, dropped. Re-order cancelled.", "content-manager.components.RepeatableComponent.error-message": "The component(s) contain error(s)", "content-manager.components.Search.placeholder": "Search for an entry...", "content-manager.components.Select.draft-info-title": "State: Draft",