From 312e88eea7c5a7d4ca382de1ce03f38517551faf Mon Sep 17 00:00:00 2001 From: HichamELBSI Date: Fri, 31 Dec 2021 17:26:13 +0100 Subject: [PATCH 01/29] CM: Add repeatable component to list view Signed-off-by: HichamELBSI --- .../lib/fixtures/store/index.js | 1 + .../CellContent/RepeatableComponent/index.js | 115 ++++++++++++++++++ .../DynamicTable/CellContent/index.js | 75 ++++++++---- .../CellContent/utils/hasContent.js | 19 +++ .../CellContent/utils/tests/hasContent.js | 48 ++++++++ .../DynamicTable/TableRows/index.js | 3 + .../components/DynamicTable/index.js | 1 + .../utils/formatLayouts.js | 18 ++- .../content-manager/pages/ListView/actions.js | 3 +- .../content-manager/pages/ListView/reducer.js | 47 ++++--- .../pages/ListView/tests/reducer.test.js | 1 + .../pages/ListViewLayoutManager/index.js | 2 +- .../utils/checkIfAttributeIsDisplayable.js | 6 +- 13 files changed, 293 insertions(+), 46 deletions(-) create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.js diff --git a/packages/admin-test-utils/lib/fixtures/store/index.js b/packages/admin-test-utils/lib/fixtures/store/index.js index ba2bba801f..3766cfc923 100644 --- a/packages/admin-test-utils/lib/fixtures/store/index.js +++ b/packages/admin-test-utils/lib/fixtures/store/index.js @@ -14,6 +14,7 @@ const reducers = { 'content-manager_listView': jest.fn(() => ({ data: [], isLoading: true, + components: [], contentType: {}, initialDisplayedHeaders: [], displayedHeaders: [], diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js new file mode 100644 index 0000000000..5d8c6d52bd --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js @@ -0,0 +1,115 @@ +import React, { useState, useRef } from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; +import { Flex } from '@strapi/design-system/Flex'; +import { Box } from '@strapi/design-system/Box'; +import { Tooltip } from '@strapi/design-system/Tooltip'; +import { Typography } from '@strapi/design-system/Typography'; +import { Popover } from '@strapi/design-system/Popover'; +import { FocusTrap } from '@strapi/design-system/FocusTrap'; +import { SortIcon, stopPropagation } from '@strapi/helper-plugin'; + +import CellValue from '../CellValue'; + +const Button = styled.button` + svg { + > g, + path { + fill: ${({ theme }) => theme.colors.neutral500}; + } + } + &:hover { + svg { + > g, + path { + fill: ${({ theme }) => theme.colors.neutral600}; + } + } + } + &:active { + svg { + > g, + path { + fill: ${({ theme }) => theme.colors.neutral400}; + } + } + } +`; + +const ActionWrapper = styled.div` + display: flex; + align-items: center; + justify-content: center; + height: ${32 / 16}rem; + width: ${32 / 16}rem; + svg { + height: ${4 / 16}rem; + } +`; + +const RepeatableComponentCell = ({ value, metadatas, component }) => { + const [visible, setVisible] = useState(false); + const buttonRef = useRef(); + const { mainField } = metadatas; + const type = component?.attributes?.[mainField]?.type; + const subValues = [...value.slice(1)]; + + const handleTogglePopover = () => setVisible(prev => !prev); + + return ( + + + + + + ); +}; + +RepeatableComponentCell.propTypes = { + metadatas: PropTypes.shape({ + mainField: PropTypes.string.isRequired, + }).isRequired, + value: PropTypes.array.isRequired, + component: PropTypes.shape({ + attributes: PropTypes.arrayOf( + PropTypes.shape({ + type: PropTypes.string.isRequired, + }) + ).isRequired, + }).isRequired, +}; + +export default RepeatableComponentCell; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js index e4d32099fe..6bbcfa5849 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js @@ -5,43 +5,57 @@ import { Typography } from '@strapi/design-system/Typography'; import Media from './Media'; import MultipleMedias from './MultipleMedias'; import Relation from './Relation'; +import RepeatableComponent from './RepeatableComponent'; import CellValue from './CellValue'; +import hasContent from './utils/hasContent'; const TypographyMaxWidth = styled(Typography)` max-width: 300px; `; -const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId }) => { - if (content === null || content === undefined) { +const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId, layout }) => { + const { type } = fieldSchema; + + if (!hasContent(type, content, metadatas, fieldSchema)) { return -; } - if (fieldSchema.type === 'media' && !fieldSchema.multiple) { - return ; - } + switch (type) { + case 'media': + if (!fieldSchema.multiple) { + return ; + } - if (fieldSchema.type === 'media' && fieldSchema.multiple) { - return ; - } + return ; - if (fieldSchema.type === 'relation') { - return ( - - ); - } + case 'relation': + return ( + + ); - return ( - - - - ); + case 'component': + return ( + + ); + + default: + return ( + + + + ); + } }; CellContent.defaultProps = { @@ -51,12 +65,19 @@ CellContent.defaultProps = { CellContent.propTypes = { content: PropTypes.any, - fieldSchema: PropTypes.shape({ multiple: PropTypes.bool, type: PropTypes.string.isRequired }) - .isRequired, + fieldSchema: PropTypes.shape({ + component: PropTypes.string, + multiple: PropTypes.bool, + type: PropTypes.string.isRequired, + repeatable: PropTypes.bool, + }).isRequired, metadatas: PropTypes.object.isRequired, name: PropTypes.string.isRequired, rowId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, queryInfos: PropTypes.shape({ endPoint: PropTypes.string.isRequired }), + layout: PropTypes.shape({ + components: PropTypes.object, + }).isRequired, }; export default CellContent; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js new file mode 100644 index 0000000000..9f90a4cf8e --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js @@ -0,0 +1,19 @@ +export default function hasContent(type, content, metadatas, fieldSchema) { + let normalizedContent = content; + + if (type === 'component') { + const { mainField } = metadatas; + + if (fieldSchema.repeatable) { + normalizedContent = content?.[0]?.[mainField]; + } else { + normalizedContent = content?.[mainField]; + } + } + + if (normalizedContent === undefined || content?.length === 0) { + normalizedContent = null; + } + + return !!normalizedContent; +} diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.js new file mode 100644 index 0000000000..cda3df4f8a --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.js @@ -0,0 +1,48 @@ +import hasContent from '../hasContent'; + +describe('hasContent', () => { + it('returns true for text content', () => { + const normalizedContent = hasContent('text', 'content'); + expect(normalizedContent).toEqual(true); + }); + + it('returns false for empty text content', () => { + const normalizedContent = hasContent('text', ''); + expect(normalizedContent).toEqual(true); + }); + + it('returns false for undefined text content', () => { + const normalizedContent = hasContent('text', undefined); + expect(normalizedContent).toEqual(true); + }); + + it('extracts content from single components with content', () => { + const normalizedContent = hasContent( + 'text', + { name: 'content' }, + { mainField: 'name' }, + { repeatable: false } + ); + expect(normalizedContent).toEqual(true); + }); + + it('extracts content from single components without content', () => { + const normalizedContent = hasContent('text', {}, { mainField: 'name' }, { repeatable: false }); + expect(normalizedContent).toEqual(false); + }); + + it('extracts content from repeatable components with content', () => { + const normalizedContent = hasContent( + 'text', + [{ name: 'content' }, { name: 'content_2' }], + { mainField: 'name' }, + { repeatable: true } + ); + expect(normalizedContent).toEqual(true); + }); + + it('extracts content from repeatable components without content', () => { + const normalizedContent = hasContent('text', [], { mainField: 'name' }, { repeatable: true }); + expect(normalizedContent).toEqual(false); + }); +}); diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/TableRows/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/TableRows/index.js index db0721b154..0c9b2c3f4b 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/TableRows/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/TableRows/index.js @@ -25,6 +25,7 @@ const TableRows = ({ withMainAction, withBulkActions, rows, + layout, }) => { const { push, @@ -90,6 +91,7 @@ const TableRows = ({ name={name} {...rest} rowId={data.id} + layout={layout} /> )} @@ -185,6 +187,7 @@ TableRows.propTypes = { onClickDelete: PropTypes.func, onSelectRow: PropTypes.func, rows: PropTypes.array, + layout: PropTypes.object.isRequired, withBulkActions: PropTypes.bool, withMainAction: PropTypes.bool, }; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/index.js index 7ca5563c80..127fff0b35 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/index.js @@ -95,6 +95,7 @@ const DynamicTable = ({ rows={rows} withBulkActions withMainAction={canDelete && isBulkable} + layout={layout} /> ); diff --git a/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js b/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js index a55ddaa496..656873e1cf 100644 --- a/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +++ b/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js @@ -40,7 +40,7 @@ const formatLayouts = (initialData, models) => { const formattedCTEditLayout = formatLayoutWithMetas(data.contentType, null, models); const ctUid = data.contentType.uid; const formattedEditRelationsLayout = formatEditRelationsLayoutWithMetas(data.contentType, models); - const formattedListLayout = formatListLayoutWithMetas(data.contentType, models); + const formattedListLayout = formatListLayoutWithMetas(data.contentType, data.components); set(data, ['contentType', 'layouts', 'edit'], formattedCTEditLayout); set(data, ['contentType', 'layouts', 'editRelations'], formattedEditRelationsLayout); @@ -146,7 +146,7 @@ const formatLayoutWithMetas = (contentTypeConfiguration, ctUid, models) => { return formatted; }; -const formatListLayoutWithMetas = contentTypeConfiguration => { +const formatListLayoutWithMetas = (contentTypeConfiguration, components) => { const formatted = contentTypeConfiguration.layouts.list.reduce((acc, current) => { const fieldSchema = get(contentTypeConfiguration, ['attributes', current], {}); const metadatas = get(contentTypeConfiguration, ['metadatas', current, 'list'], {}); @@ -164,6 +164,20 @@ const formatListLayoutWithMetas = contentTypeConfiguration => { return acc; } + if (type === 'component') { + acc.push({ + key: `__${current}_key__`, + name: current, + fieldSchema, + metadatas: { + ...metadatas, + mainField: components[fieldSchema.component].settings.mainField, + }, + }); + + return acc; + } + acc.push({ key: `__${current}_key__`, name: current, fieldSchema, metadatas }); return acc; diff --git a/packages/core/admin/admin/src/content-manager/pages/ListView/actions.js b/packages/core/admin/admin/src/content-manager/pages/ListView/actions.js index 6cac667cb6..f4aba39f76 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListView/actions.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListView/actions.js @@ -21,11 +21,12 @@ export function resetProps() { return { type: RESET_PROPS }; } -export const setLayout = contentType => { +export const setLayout = ({ components, contentType }) => { const { layouts } = contentType; return { contentType, + components, displayedHeaders: layouts.list, type: SET_LIST_LAYOUT, }; diff --git a/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js b/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js index 0a6398bfc1..de3b43f6a2 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js @@ -4,6 +4,7 @@ */ import produce from 'immer'; +import get from 'lodash/get'; import { GET_DATA, GET_DATA_SUCCEEDED, @@ -17,6 +18,7 @@ export const initialState = { data: [], isLoading: true, contentType: {}, + components: [], initialDisplayedHeaders: [], displayedHeaders: [], pagination: { @@ -26,21 +28,22 @@ export const initialState = { const listViewReducer = (state = initialState, action) => // eslint-disable-next-line consistent-return - produce(state, drafState => { + produce(state, draftState => { switch (action.type) { case GET_DATA: { return { ...initialState, contentType: state.contentType, + components: state.components, initialDisplayedHeaders: state.initialDisplayedHeaders, displayedHeaders: state.displayedHeaders, }; } case GET_DATA_SUCCEEDED: { - drafState.pagination = action.pagination; - drafState.data = action.data; - drafState.isLoading = false; + draftState.pagination = action.pagination; + draftState.data = action.data; + draftState.isLoading = false; break; } @@ -59,8 +62,23 @@ const listViewReducer = (state = initialState, action) => key: `__${name}_key__`, }; - if (attributes[name].type === 'relation') { - drafState.displayedHeaders.push({ + if (attributes[name].type === 'component') { + const componentName = attributes[name].component; + const mainField = get( + state, + ['components', componentName, 'settings', 'mainField'], + null + ); + + draftState.displayedHeaders.push({ + ...header, + metadatas: { + ...metas, + mainField, + }, + }); + } else if (attributes[name].type === 'relation') { + draftState.displayedHeaders.push({ ...header, queryInfos: { defaultParams: {}, @@ -68,10 +86,10 @@ const listViewReducer = (state = initialState, action) => }, }); } else { - drafState.displayedHeaders.push(header); + draftState.displayedHeaders.push(header); } } else { - drafState.displayedHeaders = state.displayedHeaders.filter( + draftState.displayedHeaders = state.displayedHeaders.filter( header => header.name !== name ); } @@ -79,23 +97,24 @@ const listViewReducer = (state = initialState, action) => break; } case ON_RESET_LIST_HEADERS: { - drafState.displayedHeaders = state.initialDisplayedHeaders; + draftState.displayedHeaders = state.initialDisplayedHeaders; break; } case RESET_PROPS: { return initialState; } case SET_LIST_LAYOUT: { - const { contentType, displayedHeaders } = action; + const { contentType, components, displayedHeaders } = action; - drafState.contentType = contentType; - drafState.displayedHeaders = displayedHeaders; - drafState.initialDisplayedHeaders = displayedHeaders; + draftState.contentType = contentType; + draftState.components = components; + draftState.displayedHeaders = displayedHeaders; + draftState.initialDisplayedHeaders = displayedHeaders; break; } default: - return drafState; + return draftState; } }); diff --git a/packages/core/admin/admin/src/content-manager/pages/ListView/tests/reducer.test.js b/packages/core/admin/admin/src/content-manager/pages/ListView/tests/reducer.test.js index 74fb0652cf..9a38cd1501 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListView/tests/reducer.test.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListView/tests/reducer.test.js @@ -10,6 +10,7 @@ describe('CONTENT MANAGER | CONTAINERS | ListView | reducer', () => { state = { data: [], isLoading: true, + components: [], contentType: {}, initialDisplayedHeaders: [], displayedHeaders: [], diff --git a/packages/core/admin/admin/src/content-manager/pages/ListViewLayoutManager/index.js b/packages/core/admin/admin/src/content-manager/pages/ListViewLayoutManager/index.js index 78c6fc1c55..b6c1ccdf69 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListViewLayoutManager/index.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListViewLayoutManager/index.js @@ -21,7 +21,7 @@ const ListViewLayout = ({ layout, ...props }) => { }, [rawQuery, replace, redirectionLink]); useEffect(() => { - dispatch(setLayout(layout.contentType)); + dispatch(setLayout(layout)); }, [dispatch, layout]); useEffect(() => { diff --git a/packages/core/admin/admin/src/content-manager/utils/checkIfAttributeIsDisplayable.js b/packages/core/admin/admin/src/content-manager/utils/checkIfAttributeIsDisplayable.js index cdcbf92564..d9b750e069 100644 --- a/packages/core/admin/admin/src/content-manager/utils/checkIfAttributeIsDisplayable.js +++ b/packages/core/admin/admin/src/content-manager/utils/checkIfAttributeIsDisplayable.js @@ -3,11 +3,15 @@ import { toLower } from 'lodash'; const checkIfAttributeIsDisplayable = attribute => { const type = attribute.type; + if (type === 'component') { + return attribute.repeatable; + } + if (type === 'relation') { return !toLower(attribute.relationType).includes('morph'); } - return !['json', 'component', 'dynamiczone', 'richtext', 'password'].includes(type) && !!type; + return !['json', 'dynamiczone', 'richtext', 'password'].includes(type) && !!type; }; export default checkIfAttributeIsDisplayable; From 4af0aa0b4deb8310f1773935c663a9dc469f2036 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Mon, 7 Feb 2022 20:04:38 +0100 Subject: [PATCH 02/29] CM: Allow displaying single components in list-views --- .../CellContent/SingleComponent/index.js | 40 +++++++++++++++++++ .../DynamicTable/CellContent/index.js | 13 +++++- .../utils/checkIfAttributeIsDisplayable.js | 4 -- 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js new file mode 100644 index 0000000000..f3bf8ec3b2 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js @@ -0,0 +1,40 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Flex } from '@strapi/design-system/Flex'; +import { Tooltip } from '@strapi/design-system/Tooltip'; +import { Typography } from '@strapi/design-system/Typography'; +import { stopPropagation } from '@strapi/helper-plugin'; + +import CellValue from '../CellValue'; + +const SingleComponentCell = ({ value, metadatas, component }) => { + const { mainField } = metadatas; + const content = value?.[mainField]; + const type = component?.attributes?.[mainField]?.type; + + return ( + + + + + + + + ); +}; + +SingleComponentCell.propTypes = { + metadatas: PropTypes.shape({ + mainField: PropTypes.string.isRequired, + }).isRequired, + value: PropTypes.object.isRequired, + component: PropTypes.shape({ + attributes: PropTypes.arrayOf( + PropTypes.shape({ + type: PropTypes.string.isRequired, + }) + ).isRequired, + }).isRequired, +}; + +export default SingleComponentCell; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js index 6bbcfa5849..1e3efe4ccf 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js @@ -6,6 +6,7 @@ import Media from './Media'; import MultipleMedias from './MultipleMedias'; import Relation from './Relation'; import RepeatableComponent from './RepeatableComponent'; +import SingleComponent from './SingleComponent'; import CellValue from './CellValue'; import hasContent from './utils/hasContent'; @@ -41,8 +42,18 @@ const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId, ); case 'component': + if (fieldSchema.repeatable === true) { + return ( + + ); + } + return ( - { const type = attribute.type; - if (type === 'component') { - return attribute.repeatable; - } - if (type === 'relation') { return !toLower(attribute.relationType).includes('morph'); } From 98fb9c2465020f34c3a5a827e9ef1b4f51c99d30 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Wed, 9 Feb 2022 12:37:29 +0100 Subject: [PATCH 03/29] CM: Pass required mainField data through state rather than props --- .../CellContent/RepeatableComponent/index.js | 44 ++++++++----------- .../CellContent/SingleComponent/index.js | 20 ++++----- .../DynamicTable/CellContent/index.js | 21 ++------- .../CellContent/utils/hasContent.js | 22 +++++----- .../CellContent/utils/tests/hasContent.js | 34 ++++++++++---- .../DynamicTable/TableRows/index.js | 3 -- .../components/DynamicTable/index.js | 1 - .../utils/formatLayouts.js | 9 +++- 8 files changed, 74 insertions(+), 80 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js index 5d8c6d52bd..ac4eb8ac89 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js @@ -47,12 +47,13 @@ const ActionWrapper = styled.div` } `; -const RepeatableComponentCell = ({ value, metadatas, component }) => { +const RepeatableComponentCell = ({ value, metadatas }) => { const [visible, setVisible] = useState(false); const buttonRef = useRef(); - const { mainField } = metadatas; - const type = component?.attributes?.[mainField]?.type; - const subValues = [...value.slice(1)]; + const { + mainField: { type: mainFieldType, name: mainFieldName }, + } = metadatas; + const subItems = [...value.slice(1)]; const handleTogglePopover = () => setVisible(prev => !prev); @@ -66,26 +67,22 @@ const RepeatableComponentCell = ({ value, metadatas, component }) => { textColor="neutral800" ellipsis > - + - {visible && subValues.length > 0 && ( + {visible && subItems.length > 0 && (
    - {subValues.map(entry => { - const entryType = component?.attributes?.[mainField]?.type; - - return ( - - - - - - ); - })} + {subItems.map(item => ( + + + + + + ))}
@@ -100,16 +97,13 @@ const RepeatableComponentCell = ({ value, metadatas, component }) => { RepeatableComponentCell.propTypes = { metadatas: PropTypes.shape({ - mainField: PropTypes.string.isRequired, + mainField: PropTypes.shape({ + name: PropTypes.string, + type: PropTypes.string, + value: PropTypes.string, + }), }).isRequired, value: PropTypes.array.isRequired, - component: PropTypes.shape({ - attributes: PropTypes.arrayOf( - PropTypes.shape({ - type: PropTypes.string.isRequired, - }) - ).isRequired, - }).isRequired, }; export default RepeatableComponentCell; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js index f3bf8ec3b2..50a1ec240e 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js @@ -7,16 +7,15 @@ import { stopPropagation } from '@strapi/helper-plugin'; import CellValue from '../CellValue'; -const SingleComponentCell = ({ value, metadatas, component }) => { +const SingleComponentCell = ({ value, metadatas }) => { const { mainField } = metadatas; - const content = value?.[mainField]; - const type = component?.attributes?.[mainField]?.type; + const content = value[mainField.name]; return ( - + @@ -25,16 +24,13 @@ const SingleComponentCell = ({ value, metadatas, component }) => { SingleComponentCell.propTypes = { metadatas: PropTypes.shape({ - mainField: PropTypes.string.isRequired, + mainField: PropTypes.shape({ + name: PropTypes.string, + type: PropTypes.string, + value: PropTypes.string, + }), }).isRequired, value: PropTypes.object.isRequired, - component: PropTypes.shape({ - attributes: PropTypes.arrayOf( - PropTypes.shape({ - type: PropTypes.string.isRequired, - }) - ).isRequired, - }).isRequired, }; export default SingleComponentCell; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js index 1e3efe4ccf..cd64e6fb3c 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js @@ -14,7 +14,7 @@ const TypographyMaxWidth = styled(Typography)` max-width: 300px; `; -const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId, layout }) => { +const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId }) => { const { type } = fieldSchema; if (!hasContent(type, content, metadatas, fieldSchema)) { @@ -43,22 +43,10 @@ const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId, case 'component': if (fieldSchema.repeatable === true) { - return ( - - ); + return ; } - return ( - - ); + return ; default: return ( @@ -86,9 +74,6 @@ CellContent.propTypes = { name: PropTypes.string.isRequired, rowId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, queryInfos: PropTypes.shape({ endPoint: PropTypes.string.isRequired }), - layout: PropTypes.shape({ - components: PropTypes.object, - }).isRequired, }; export default CellContent; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js index 9f90a4cf8e..81f39a7b1f 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js @@ -1,19 +1,17 @@ +import isEmpty from 'lodash/isEmpty'; + export default function hasContent(type, content, metadatas, fieldSchema) { - let normalizedContent = content; - if (type === 'component') { - const { mainField } = metadatas; + const { + mainField: { name: mainFieldName }, + } = metadatas; - if (fieldSchema.repeatable) { - normalizedContent = content?.[0]?.[mainField]; - } else { - normalizedContent = content?.[mainField]; + if (fieldSchema?.repeatable) { + return content.some(item => !isEmpty(item[mainFieldName])); } + + return !isEmpty(content[mainFieldName]); } - if (normalizedContent === undefined || content?.length === 0) { - normalizedContent = null; - } - - return !!normalizedContent; + return !isEmpty(content); } diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.js index cda3df4f8a..3481426627 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.js @@ -18,31 +18,49 @@ describe('hasContent', () => { it('extracts content from single components with content', () => { const normalizedContent = hasContent( - 'text', + 'component', { name: 'content' }, - { mainField: 'name' }, - { repeatable: false } + { mainField: { name: 'content', value: 'something' } } ); expect(normalizedContent).toEqual(true); }); it('extracts content from single components without content', () => { - const normalizedContent = hasContent('text', {}, { mainField: 'name' }, { repeatable: false }); + const normalizedContent = hasContent( + 'component', + { name: 'content' }, + { mainField: { name: 'content', value: '' } } + ); expect(normalizedContent).toEqual(false); }); it('extracts content from repeatable components with content', () => { const normalizedContent = hasContent( - 'text', - [{ name: 'content' }, { name: 'content_2' }], - { mainField: 'name' }, + 'component', + [{ name: 'content_2', value: 'truthy' }], + { mainField: { name: 'content_2' } }, { repeatable: true } ); expect(normalizedContent).toEqual(true); }); it('extracts content from repeatable components without content', () => { - const normalizedContent = hasContent('text', [], { mainField: 'name' }, { repeatable: true }); + const normalizedContent = hasContent( + 'component', + [{ name: 'content_2', value: '' }], + { mainField: { name: 'content_2' } }, + { repeatable: true } + ); + expect(normalizedContent).toEqual(false); + }); + + it('extracts content from repeatable components without content in the first component', () => { + const normalizedContent = hasContent( + 'component', + [{ name: 'content_2', value: '' }, { name: 'content_2', value: 'something' }], + { mainField: { name: 'content_2' } }, + { repeatable: true } + ); expect(normalizedContent).toEqual(false); }); }); diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/TableRows/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/TableRows/index.js index 0c9b2c3f4b..db0721b154 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/TableRows/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/TableRows/index.js @@ -25,7 +25,6 @@ const TableRows = ({ withMainAction, withBulkActions, rows, - layout, }) => { const { push, @@ -91,7 +90,6 @@ const TableRows = ({ name={name} {...rest} rowId={data.id} - layout={layout} /> )} @@ -187,7 +185,6 @@ TableRows.propTypes = { onClickDelete: PropTypes.func, onSelectRow: PropTypes.func, rows: PropTypes.array, - layout: PropTypes.object.isRequired, withBulkActions: PropTypes.bool, withMainAction: PropTypes.bool, }; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/index.js index 127fff0b35..7ca5563c80 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/index.js @@ -95,7 +95,6 @@ const DynamicTable = ({ rows={rows} withBulkActions withMainAction={canDelete && isBulkable} - layout={layout} /> ); diff --git a/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js b/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js index 656873e1cf..de44f1291d 100644 --- a/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +++ b/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js @@ -165,13 +165,20 @@ const formatListLayoutWithMetas = (contentTypeConfiguration, components) => { } if (type === 'component') { + const component = components[fieldSchema.component]; + const mainFieldName = component.settings.mainField; + const mainFieldAttributes = component.attributes[mainFieldName]; + acc.push({ key: `__${current}_key__`, name: current, fieldSchema, metadatas: { ...metadatas, - mainField: components[fieldSchema.component].settings.mainField, + mainField: { + name: mainFieldName, + ...mainFieldAttributes, + }, }, }); From 01880b6941b5b6d09fa427298f9fd3cdde5212b2 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Wed, 9 Feb 2022 13:22:36 +0100 Subject: [PATCH 04/29] CM: Replace static string with a translation --- .../CellContent/RepeatableComponent/index.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js index ac4eb8ac89..384a8f3b2f 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js @@ -1,6 +1,7 @@ import React, { useState, useRef } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; +import { useIntl } from 'react-intl'; import { Flex } from '@strapi/design-system/Flex'; import { Box } from '@strapi/design-system/Box'; import { Tooltip } from '@strapi/design-system/Tooltip'; @@ -8,6 +9,7 @@ import { Typography } from '@strapi/design-system/Typography'; import { Popover } from '@strapi/design-system/Popover'; import { FocusTrap } from '@strapi/design-system/FocusTrap'; import { SortIcon, stopPropagation } from '@strapi/helper-plugin'; +import { getTrad } from '../../../../utils'; import CellValue from '../CellValue'; @@ -49,6 +51,7 @@ const ActionWrapper = styled.div` const RepeatableComponentCell = ({ value, metadatas }) => { const [visible, setVisible] = useState(false); + const { formatMessage } = useIntl(); const buttonRef = useRef(); const { mainField: { type: mainFieldType, name: mainFieldName }, @@ -59,7 +62,12 @@ const RepeatableComponentCell = ({ value, metadatas }) => { return ( - + - - + + ))} + + ); }; From c5c96374658611bb76c73fca90b0b772fbefd09e Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Tue, 15 Feb 2022 10:06:36 +0100 Subject: [PATCH 11/29] ListView: unify mainField state with formatLayout --- .../content-manager/pages/ListView/reducer.js | 63 ++++++++++++------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js b/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js index de3b43f6a2..bfeb230f50 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js @@ -62,31 +62,46 @@ const listViewReducer = (state = initialState, action) => key: `__${name}_key__`, }; - if (attributes[name].type === 'component') { - const componentName = attributes[name].component; - const mainField = get( - state, - ['components', componentName, 'settings', 'mainField'], - null - ); + switch (attributes[name].type) { + case 'component': { + const componentName = attributes[name].component; + const mainFieldName = get( + state, + ['components', componentName, 'settings', 'mainField'], + null + ); + const mainFieldAttributes = get(state, [ + 'components', + componentName, + 'attributes', + mainFieldName, + ]); - draftState.displayedHeaders.push({ - ...header, - metadatas: { - ...metas, - mainField, - }, - }); - } else if (attributes[name].type === 'relation') { - draftState.displayedHeaders.push({ - ...header, - queryInfos: { - defaultParams: {}, - endPoint: `collection-types/${uid}`, - }, - }); - } else { - draftState.displayedHeaders.push(header); + draftState.displayedHeaders.push({ + ...header, + metadatas: { + ...metas, + mainField: { + ...mainFieldAttributes, + name: mainFieldName, + }, + }, + }); + break; + } + + case 'relation': + draftState.displayedHeaders.push({ + ...header, + queryInfos: { + defaultParams: {}, + endPoint: `collection-types/${uid}`, + }, + }); + break; + + default: + draftState.displayedHeaders.push(header); } } else { draftState.displayedHeaders = state.displayedHeaders.filter( From 7bcfbbe40f6a172a01f8173de791cf88dccd885b Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Tue, 15 Feb 2022 12:50:35 +0100 Subject: [PATCH 12/29] CellContent: Update hasContent() tests --- .../CellContent/utils/tests/hasContent.test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js index f24a3d8747..d3c6dbe534 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js @@ -8,19 +8,19 @@ describe('hasContent', () => { it('returns false for empty text content', () => { const normalizedContent = hasContent('text', ''); - expect(normalizedContent).toEqual(true); + expect(normalizedContent).toEqual(false); }); it('returns false for undefined text content', () => { const normalizedContent = hasContent('text', undefined); - expect(normalizedContent).toEqual(true); + expect(normalizedContent).toEqual(false); }); it('extracts content from single components with content', () => { const normalizedContent = hasContent( 'component', { name: 'content' }, - { mainField: { name: 'content', value: 'something' } } + { mainField: { name: 'name' } } ); expect(normalizedContent).toEqual(true); }); @@ -28,8 +28,8 @@ describe('hasContent', () => { it('extracts content from single components without content', () => { const normalizedContent = hasContent( 'component', - { name: 'content' }, - { mainField: { name: 'content', value: '' } } + { name: '' }, + { mainField: { name: 'name' } } ); expect(normalizedContent).toEqual(false); }); From 5925892936994b26e78cce3c036552cb76ece64c Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Tue, 22 Feb 2022 11:10:59 +0100 Subject: [PATCH 13/29] RepeatableComponent: inline helper functions --- .../CellContent/RepeatableComponent/index.js | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js index 0ac37d3bf6..276515678f 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js @@ -9,18 +9,6 @@ import { stopPropagation } from '@strapi/helper-plugin'; import CellValue from '../CellValue'; -function getMainFieldValue(field, name) { - return field[name] || field.id; -} - -function getMainFieldType(field, name, defaultType) { - if (field[name]) { - return defaultType; - } - - return 'text'; -} - const RepeatableComponentCell = ({ value, metadatas }) => { const { formatMessage } = useIntl(); const { @@ -46,10 +34,7 @@ const RepeatableComponentCell = ({ value, metadatas }) => { {value.map(item => ( - + ))} From 08fe81bf0565a488ed05fecaa29762bf5a9d3576 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Tue, 22 Feb 2022 11:31:14 +0100 Subject: [PATCH 14/29] Rename mainFieldAttributes -> mainFieldAttribute --- .../hooks/useFetchContentTypeLayout/utils/formatLayouts.js | 4 ++-- .../admin/admin/src/content-manager/pages/ListView/reducer.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js b/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js index de44f1291d..6ae3d0e015 100644 --- a/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +++ b/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js @@ -167,7 +167,7 @@ const formatListLayoutWithMetas = (contentTypeConfiguration, components) => { if (type === 'component') { const component = components[fieldSchema.component]; const mainFieldName = component.settings.mainField; - const mainFieldAttributes = component.attributes[mainFieldName]; + const mainFieldAttribute = component.attributes[mainFieldName]; acc.push({ key: `__${current}_key__`, @@ -176,8 +176,8 @@ const formatListLayoutWithMetas = (contentTypeConfiguration, components) => { metadatas: { ...metadatas, mainField: { + ...mainFieldAttribute, name: mainFieldName, - ...mainFieldAttributes, }, }, }); diff --git a/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js b/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js index bfeb230f50..f1efaf3fb4 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListView/reducer.js @@ -70,7 +70,7 @@ const listViewReducer = (state = initialState, action) => ['components', componentName, 'settings', 'mainField'], null ); - const mainFieldAttributes = get(state, [ + const mainFieldAttribute = get(state, [ 'components', componentName, 'attributes', @@ -82,7 +82,7 @@ const listViewReducer = (state = initialState, action) => metadatas: { ...metas, mainField: { - ...mainFieldAttributes, + ...mainFieldAttribute, name: mainFieldName, }, }, From c9df39e72fdf51565aebe6c247d2ceb2b8e4b65d Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Tue, 22 Feb 2022 11:51:12 +0100 Subject: [PATCH 15/29] CellContent: Adapt tests to the fact that id is always present --- .../CellContent/utils/hasContent.js | 4 +++- .../utils/tests/hasContent.test.js | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js index 2b6757acca..988ed8b31e 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js @@ -6,8 +6,10 @@ export default function hasContent(type, content, metadatas, fieldSchema) { mainField: { name: mainFieldName }, } = metadatas; + // Repeatable fields show the ID as fallback, in case the mainField + // doesn't have any content if (fieldSchema?.repeatable) { - return content.some(item => !isEmpty(item)); + return content.length > 0; } return !isEmpty(content[mainFieldName]); diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js index d3c6dbe534..b43856546b 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js @@ -19,7 +19,7 @@ describe('hasContent', () => { it('extracts content from single components with content', () => { const normalizedContent = hasContent( 'component', - { name: 'content' }, + { name: 'content', id: 1 }, { mainField: { name: 'name' } } ); expect(normalizedContent).toEqual(true); @@ -28,7 +28,7 @@ describe('hasContent', () => { it('extracts content from single components without content', () => { const normalizedContent = hasContent( 'component', - { name: '' }, + { name: '', id: 1 }, { mainField: { name: 'name' } } ); expect(normalizedContent).toEqual(false); @@ -37,7 +37,7 @@ describe('hasContent', () => { it('extracts content from repeatable components with content', () => { const normalizedContent = hasContent( 'component', - [{ name: 'content_2', value: 'truthy' }], + [{ name: 'content_2', value: 'truthy', id: 1 }], { mainField: { name: 'content_2' } }, { repeatable: true } ); @@ -47,7 +47,7 @@ describe('hasContent', () => { it('extracts content from repeatable components without content', () => { const normalizedContent = hasContent( 'component', - [{ name: 'content_2', value: '' }], + [{ name: 'content_2', value: '', id: 1 }], { mainField: { name: 'content_2' } }, { repeatable: true } ); @@ -57,7 +57,17 @@ describe('hasContent', () => { it('extracts content from repeatable components without content', () => { const normalizedContent = hasContent( 'component', - [{}, {}], + [{ id: 1 }, { id: 2 }], + { mainField: { name: 'content_2' } }, + { repeatable: true } + ); + expect(normalizedContent).toEqual(true); + }); + + it('extracts content from repeatable components without content', () => { + const normalizedContent = hasContent( + 'component', + [], { mainField: { name: 'content_2' } }, { repeatable: true } ); From 933c5c0ffe7b749238c95d081da62c6b6a92fbe4 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Tue, 22 Feb 2022 13:20:21 +0100 Subject: [PATCH 16/29] Add max-width to component values --- .../CellContent/RepeatableComponent/index.js | 9 +++++++-- .../DynamicTable/CellContent/SingleComponent/index.js | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js index 276515678f..520fd9b45e 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { Badge } from '@strapi/design-system/Badge'; import { Box } from '@strapi/design-system/Box'; @@ -9,6 +10,10 @@ import { stopPropagation } from '@strapi/helper-plugin'; import CellValue from '../CellValue'; +const TypographyMaxWidth = styled(Typography)` + max-width: 500px; +`; + const RepeatableComponentCell = ({ value, metadatas }) => { const { formatMessage } = useIntl(); const { @@ -33,9 +38,9 @@ const RepeatableComponentCell = ({ value, metadatas }) => { {value.map(item => ( - + - + ))} diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js index 7b8f9b0874..3d9832fed8 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/SingleComponent/index.js @@ -1,19 +1,24 @@ import React from 'react'; import PropTypes from 'prop-types'; +import styled from 'styled-components'; import { Tooltip } from '@strapi/design-system/Tooltip'; import { Typography } from '@strapi/design-system/Typography'; import CellValue from '../CellValue'; +const TypographyMaxWidth = styled(Typography)` + max-width: 250px; +`; + const SingleComponentCell = ({ value, metadatas }) => { const { mainField } = metadatas; const content = value[mainField.name]; return ( - + - + ); }; From 4f0516c0d8852068d182eb6301871da1d52c5a07 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Wed, 23 Feb 2022 15:56:37 +0100 Subject: [PATCH 17/29] RepeatableComponent: Improve alignment of the SimpleMenu --- .../CellContent/RepeatableComponent/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js index 520fd9b45e..f492d6890b 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js @@ -14,6 +14,11 @@ const TypographyMaxWidth = styled(Typography)` max-width: 500px; `; +const SimpleMenuAdapted = styled(SimpleMenu)` + margin-left: -6px; + padding-left: 4px; +`; + const RepeatableComponentCell = ({ value, metadatas }) => { const { formatMessage } = useIntl(); const { @@ -35,7 +40,7 @@ const RepeatableComponentCell = ({ value, metadatas }) => { return ( - + {value.map(item => ( @@ -43,7 +48,7 @@ const RepeatableComponentCell = ({ value, metadatas }) => { ))} - + ); }; From d5d425a359b568b4704fedb1c5d0d4578563fc28 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Fri, 11 Mar 2022 14:15:51 +0100 Subject: [PATCH 18/29] RepeatableComponent: Use small variant of the SimpleMenu --- .../CellContent/RepeatableComponent/index.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js index f492d6890b..518f752cf8 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RepeatableComponent/index.js @@ -14,11 +14,6 @@ const TypographyMaxWidth = styled(Typography)` max-width: 500px; `; -const SimpleMenuAdapted = styled(SimpleMenu)` - margin-left: -6px; - padding-left: 4px; -`; - const RepeatableComponentCell = ({ value, metadatas }) => { const { formatMessage } = useIntl(); const { @@ -40,7 +35,7 @@ const RepeatableComponentCell = ({ value, metadatas }) => { return ( - + {value.map(item => ( @@ -48,7 +43,7 @@ const RepeatableComponentCell = ({ value, metadatas }) => { ))} - + ); }; From 59e72eae686207b256749b1dddbc148eb81f5f37 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Wed, 23 Feb 2022 15:00:39 +0100 Subject: [PATCH 19/29] Prepare the hasContent utility to handle relations --- .../DynamicTable/CellContent/utils/hasContent.js | 4 ++++ .../CellContent/utils/tests/hasContent.test.js | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js index 988ed8b31e..718fe9287b 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js @@ -15,5 +15,9 @@ export default function hasContent(type, content, metadatas, fieldSchema) { return !isEmpty(content[mainFieldName]); } + if (type === 'relation') { + return content.count > 0; + } + return !isEmpty(content); } diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js index b43856546b..ecc032bac0 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js @@ -73,4 +73,14 @@ describe('hasContent', () => { ); expect(normalizedContent).toEqual(false); }); + + it('extracts content from relations with content', () => { + const normalizedContent = hasContent('relation', { count: 1 }); + expect(normalizedContent).toEqual(true); + }); + + it('extracts content from relations without content', () => { + const normalizedContent = hasContent('relation', { count: 0 }); + expect(normalizedContent).toEqual(false); + }); }); From d0b6274d4b39f6d558bca7dba74c81143541a719 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Wed, 23 Feb 2022 15:01:13 +0100 Subject: [PATCH 20/29] CellContent: Refactor relations to use the SimpleMenu component --- .../CellContent/Relation/PopoverContent.js | 87 ----------- .../CellContent/Relation/index.js | 145 ++++++++++-------- 2 files changed, 84 insertions(+), 148 deletions(-) delete mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/PopoverContent.js diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/PopoverContent.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/PopoverContent.js deleted file mode 100644 index eca07fe300..0000000000 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/PopoverContent.js +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import { useQuery } from 'react-query'; -import PropTypes from 'prop-types'; -import { useIntl } from 'react-intl'; -import { Box } from '@strapi/design-system/Box'; -import { Typography } from '@strapi/design-system/Typography'; -import { Loader } from '@strapi/design-system/Loader'; -import { useNotifyAT } from '@strapi/design-system/LiveRegions'; -import { axiosInstance } from '../../../../../core/utils'; -import { getRequestUrl, getTrad } from '../../../../utils'; -import CellValue from '../CellValue'; - -const fetchRelation = async (endPoint, notifyStatus) => { - const { - data: { results, pagination }, - } = await axiosInstance.get(endPoint); - - notifyStatus(); - - return { results, pagination }; -}; - -const PopoverContent = ({ fieldSchema, name, rowId, targetModel, queryInfos }) => { - const requestURL = getRequestUrl(`${queryInfos.endPoint}/${rowId}/${name.split('.')[0]}`); - const { notifyStatus } = useNotifyAT(); - const { formatMessage } = useIntl(); - - const notify = () => { - const message = formatMessage({ - id: getTrad('DynamicTable.relation-loaded'), - defaultMessage: 'The relations have been loaded', - }); - notifyStatus(message); - }; - - const { data, status } = useQuery([targetModel, rowId], () => fetchRelation(requestURL, notify), { - staleTime: 0, - }); - - if (status !== 'success') { - return ( - - Loading content - - ); - } - - return ( -
    - {data?.results.map(entry => { - const value = entry[fieldSchema.name]; - - return ( - - - {value ? ( - - ) : ( - '-' - )} - - - ); - })} - {data?.pagination.total > 10 && ( - - [...] - - )} -
- ); -}; - -PopoverContent.propTypes = { - fieldSchema: PropTypes.shape({ - name: PropTypes.string, - schema: PropTypes.shape({ type: PropTypes.string }).isRequired, - }).isRequired, - name: PropTypes.string.isRequired, - rowId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - targetModel: PropTypes.string.isRequired, - queryInfos: PropTypes.shape({ - endPoint: PropTypes.string, - }).isRequired, -}; - -export default PopoverContent; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js index 404e8d2a6b..a0333e773f 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js @@ -1,88 +1,111 @@ -import React, { useState, useRef } from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; +import { useQuery } from 'react-query'; import { useIntl } from 'react-intl'; -import { IconButton } from '@strapi/design-system/IconButton'; import { Typography } from '@strapi/design-system/Typography'; import { Box } from '@strapi/design-system/Box'; import { Badge } from '@strapi/design-system/Badge'; -import { Flex } from '@strapi/design-system/Flex'; -import { Popover } from '@strapi/design-system/Popover'; -import { SortIcon, stopPropagation } from '@strapi/helper-plugin'; +import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu'; import styled from 'styled-components'; -import PopoverContent from './PopoverContent'; +import { useNotifyAT } from '@strapi/design-system/LiveRegions'; +import { stopPropagation } from '@strapi/helper-plugin'; import CellValue from '../CellValue'; +import { axiosInstance } from '../../../../../core/utils'; +import { getRequestUrl, getTrad } from '../../../../utils'; const SINGLE_RELATIONS = ['oneToOne', 'manyToOne']; -const ActionWrapper = styled.span` - svg { - height: ${4 / 16}rem; - } +const TypographyMaxWidth = styled(Typography)` + max-width: 500px; `; -const RelationCountBadge = styled(Badge)` - display: flex; - align-items: center; - height: ${20 / 16}rem; - width: ${16 / 16}rem; -`; +const fetchRelation = async (endPoint, notifyStatus) => { + const { + data: { results, pagination }, + } = await axiosInstance.get(endPoint); + + notifyStatus(); + + return { results, pagination }; +}; const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => { const { formatMessage } = useIntl(); - const [visible, setVisible] = useState(false); - const buttonRef = useRef(); + const { notifyStatus } = useNotifyAT(); + const requestURL = getRequestUrl(`${queryInfos.endPoint}/${rowId}/${name.split('.')[0]}`); + const [isOpen, setIsOpen] = useState(false); + + const Label = ( + <> + {value.count}{' '} + {formatMessage( + { + id: 'content-manager.containers.ListPage.items', + defaultMessage: '{number, plural, =0 {items} one {item} other {items}}', + }, + { number: value.count } + )} + + ); + + const notify = () => { + const message = formatMessage({ + id: getTrad('DynamicTable.relation-loaded'), + defaultMessage: 'The relations have been loaded', + }); + notifyStatus(message); + }; + + const { data, status } = useQuery( + [fieldSchema.targetModel, rowId], + () => fetchRelation(requestURL, notify), + { + staleTime: 0, + }, + { + enabled: isOpen, + } + ); if (SINGLE_RELATIONS.includes(fieldSchema.relation)) { return ( - + ); } - const handleTogglePopover = () => setVisible(prev => !prev); - return ( - - {value.count} - - - {formatMessage( - { - id: 'content-manager.containers.ListPage.items', - defaultMessage: '{number, plural, =0 {items} one {item} other {items}}', - }, - { number: value.count } - )} - - - {value.count > 0 && ( - - } - /> - {visible && ( - - - - )} - - )} - + + setIsOpen(true)} onClose={() => setIsOpen(false)}> + {status !== 'success' && ( + + Loading ... + + )} + + {status === 'success' && + data?.results.map(entry => ( + + + + + + ))} + + {status === 'success' && data?.pagination.total > 10 && ( + + [...] + + )} + + ); }; From a5cd9f553c2ca2ab7a87359845ae96e6f4ea685f Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Wed, 23 Feb 2022 15:59:35 +0100 Subject: [PATCH 21/29] Relation: Improve alignment of the SimpleMenu --- .../DynamicTable/CellContent/Relation/index.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js index a0333e773f..f53ca2e2a1 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js @@ -19,6 +19,11 @@ const TypographyMaxWidth = styled(Typography)` max-width: 500px; `; +const SimpleMenuAdapted = styled(SimpleMenu)` + margin-left: -6px; + padding-left: 4px; +`; + const fetchRelation = async (endPoint, notifyStatus) => { const { data: { results, pagination }, @@ -80,7 +85,11 @@ const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => return ( - setIsOpen(true)} onClose={() => setIsOpen(false)}> + setIsOpen(true)} + onClose={() => setIsOpen(false)} + > {status !== 'success' && ( Loading ... @@ -104,7 +113,7 @@ const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => [...] )} - + ); }; From 80527907cf00347cdb408cb4568947730064ff4c Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Fri, 11 Mar 2022 14:36:13 +0100 Subject: [PATCH 22/29] Relation: Make all strings translateable, Use small SimpleMenu --- .../CellContent/Relation/index.js | 32 +++++++++++-------- .../core/admin/admin/src/translations/en.json | 2 ++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js index f53ca2e2a1..7fa999ebc1 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js @@ -6,6 +6,7 @@ import { Typography } from '@strapi/design-system/Typography'; import { Box } from '@strapi/design-system/Box'; import { Badge } from '@strapi/design-system/Badge'; import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu'; +import { Loader } from '@strapi/design-system/Loader'; import styled from 'styled-components'; import { useNotifyAT } from '@strapi/design-system/LiveRegions'; import { stopPropagation } from '@strapi/helper-plugin'; @@ -19,11 +20,6 @@ const TypographyMaxWidth = styled(Typography)` max-width: 500px; `; -const SimpleMenuAdapted = styled(SimpleMenu)` - margin-left: -6px; - padding-left: 4px; -`; - const fetchRelation = async (endPoint, notifyStatus) => { const { data: { results, pagination }, @@ -64,11 +60,9 @@ const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => const { data, status } = useQuery( [fieldSchema.targetModel, rowId], () => fetchRelation(requestURL, notify), - { - staleTime: 0, - }, { enabled: isOpen, + staleTime: 0, } ); @@ -85,14 +79,20 @@ const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => return ( - setIsOpen(true)} onClose={() => setIsOpen(false)} > {status !== 'success' && ( - Loading ... + + {formatMessage({ + id: getTrad('DynamicTable.relation-loading'), + defaultMessage: 'The relations are loading', + })} + )} @@ -109,11 +109,17 @@ const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => ))} {status === 'success' && data?.pagination.total > 10 && ( - - [...] + + ... )} - + ); }; diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json index ffa8a9f8c1..a26a70883e 100644 --- a/packages/core/admin/admin/src/translations/en.json +++ b/packages/core/admin/admin/src/translations/en.json @@ -486,6 +486,8 @@ "components.popUpWarning.title": "Please confirm", "content-manager.App.schemas.data-loaded": "The schemas have been successfully loaded", "content-manager.DynamicTable.relation-loaded": "The relations have been loaded", + "content-manager.DynamicTable.relation-loading": "The relations are loading", + "content-manager.DynamicTable.relation-more": "This relation contains more entities than displayed", "content-manager.EditRelations.title": "Relational data", "content-manager.HeaderLayout.button.label-add-entry": "Create new entry", "content-manager.api.id": "API ID", From eb8d57a37baaff5ea3ee183f52c25d860f4e5e37 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Mon, 14 Mar 2022 11:40:50 +0100 Subject: [PATCH 23/29] Relation: add tests --- .../tests/__snapshots__/index.test.js.snap | 291 ++++++++++++++++++ .../CellContent/Relation/tests/index.test.js | 80 +++++ 2 files changed, 371 insertions(+) create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/__snapshots__/index.test.js.snap create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/index.test.js diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/__snapshots__/index.test.js.snap b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/__snapshots__/index.test.js.snap new file mode 100644 index 0000000000..793fddf747 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/__snapshots__/index.test.js.snap @@ -0,0 +1,291 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DynamicTabe / Cellcontent / Relation renders and matches the snapshot 1`] = ` +.c11 { + border: 0; + -webkit-clip: rect(0 0 0 0); + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +.c5 { + background: #f6f6f9; + padding: 4px; + border-radius: 4px; + min-width: 20px; +} + +.c6 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c7 { + color: #666687; + font-weight: 600; + font-size: 0.6875rem; + line-height: 1.45; + text-transform: uppercase; +} + +.c4 { + font-weight: 600; + color: #32324d; + font-size: 0.75rem; + line-height: 1.33; +} + +.c9 { + padding-left: 8px; +} + +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + cursor: pointer; + padding: 8px; + border-radius: 4px; + background: #ffffff; + border: 1px solid #dcdce4; + position: relative; + outline: none; +} + +.c0 svg { + height: 12px; + width: 12px; +} + +.c0 svg > g, +.c0 svg path { + fill: #ffffff; +} + +.c0[aria-disabled='true'] { + pointer-events: none; +} + +.c0:after { + -webkit-transition-property: all; + transition-property: all; + -webkit-transition-duration: 0.2s; + transition-duration: 0.2s; + border-radius: 8px; + content: ''; + position: absolute; + top: -4px; + bottom: -4px; + left: -4px; + right: -4px; + border: 2px solid transparent; +} + +.c0:focus-visible { + outline: none; +} + +.c0:focus-visible:after { + border-radius: 8px; + content: ''; + position: absolute; + top: -5px; + bottom: -5px; + left: -5px; + right: -5px; + border: 2px solid #4945ff; +} + +.c1 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 8px 16px; + background: #4945ff; + border: none; + border: 1px solid transparent; + background: transparent; +} + +.c1 .c8 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c1 .c3 { + color: #ffffff; +} + +.c1[aria-disabled='true'] { + border: 1px solid #dcdce4; + background: #eaeaef; +} + +.c1[aria-disabled='true'] .c3 { + color: #666687; +} + +.c1[aria-disabled='true'] svg > g, +.c1[aria-disabled='true'] svg path { + fill: #666687; +} + +.c1[aria-disabled='true']:active { + border: 1px solid #dcdce4; + background: #eaeaef; +} + +.c1[aria-disabled='true']:active .c3 { + color: #666687; +} + +.c1[aria-disabled='true']:active svg > g, +.c1[aria-disabled='true']:active svg path { + fill: #666687; +} + +.c1:hover { + background-color: #f6f6f9; +} + +.c1:active { + border: 1px solid undefined; + background: undefined; +} + +.c1 .c3 { + color: #32324d; +} + +.c1 svg > g, +.c1 svg path { + fill: #8e8ea9; +} + +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c10 svg { + height: 4px; + width: 6px; +} + +.c2 { + padding: 4px 12px; +} + +
+ +
+

+

+

+
+`; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/index.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/index.test.js new file mode 100644 index 0000000000..9318b9c486 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/index.test.js @@ -0,0 +1,80 @@ +import React from 'react'; +import { render, fireEvent, screen, waitFor } from '@testing-library/react'; +import { ThemeProvider, lightTheme } from '@strapi/design-system'; +import { IntlProvider } from 'react-intl'; +import { QueryClientProvider, QueryClient } from 'react-query'; + +import { axiosInstance } from '../../../../../../core/utils'; +import Relation from '../index'; + +jest.spyOn(axiosInstance, 'get').mockResolvedValue({ + data: { + results: [ + { + id: 1, + name: 'relation entity 1', + }, + ], + + pagination: { + total: 1, + }, + }, +}); + +const DEFAULT_PROPS_FIXTURE = { + fieldSchema: { + type: 'relation', + relation: 'manyToMany', + target: 'api::category.category', + }, + queryInfos: { + endPoint: 'collection-types/api::address.address', + }, + metadatas: { + mainField: { + name: 'name', + schema: { + type: 'string', + }, + }, + }, + value: { + count: 1, + }, + name: 'categories.name', + rowId: 1, +}; + +const ComponentFixture = () => { + const queryClient = new QueryClient(); + + return ( + + + + + + + + ); +}; + +describe('DynamicTabe / Cellcontent / Relation', () => { + it('renders and matches the snapshot', async () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + expect(axiosInstance.get).toHaveBeenCalledTimes(0); + }); + + it('fetches relation entities once the menu is opened', async () => { + const { container } = render(); + const button = container.querySelector('[type=button]'); + + fireEvent(button, new MouseEvent('mousedown', { bubbles: true })); + + expect(screen.getByText('The relations are loading')).toBeInTheDocument(); + expect(axiosInstance.get).toHaveBeenCalledTimes(1); + await waitFor(() => expect(screen.getByText('relation entity 1')).toBeInTheDocument()); + }); +}); From 00e63ebd7d9c7510ce511fcc3e08ec27a9334e0e Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Mon, 14 Mar 2022 11:53:09 +0100 Subject: [PATCH 24/29] Relation: Improve copy --- .../components/DynamicTable/CellContent/Relation/index.js | 4 ++-- packages/core/admin/admin/src/translations/en.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js index 7fa999ebc1..d36ccf51e5 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js @@ -52,7 +52,7 @@ const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => const notify = () => { const message = formatMessage({ id: getTrad('DynamicTable.relation-loaded'), - defaultMessage: 'The relations have been loaded', + defaultMessage: 'Relations have been loaded', }); notifyStatus(message); }; @@ -90,7 +90,7 @@ const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => {formatMessage({ id: getTrad('DynamicTable.relation-loading'), - defaultMessage: 'The relations are loading', + defaultMessage: 'Relations are loading', })} diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json index a26a70883e..7e5e2f9821 100644 --- a/packages/core/admin/admin/src/translations/en.json +++ b/packages/core/admin/admin/src/translations/en.json @@ -485,8 +485,8 @@ "components.popUpWarning.message": "Are you sure you want to delete this?", "components.popUpWarning.title": "Please confirm", "content-manager.App.schemas.data-loaded": "The schemas have been successfully loaded", - "content-manager.DynamicTable.relation-loaded": "The relations have been loaded", - "content-manager.DynamicTable.relation-loading": "The relations are loading", + "content-manager.DynamicTable.relation-loaded": "Relations have been loaded", + "content-manager.DynamicTable.relation-loading": "Relations are loading", "content-manager.DynamicTable.relation-more": "This relation contains more entities than displayed", "content-manager.EditRelations.title": "Relational data", "content-manager.HeaderLayout.button.label-add-entry": "Create new entry", From 47644c0863ad4dc231a5564ce83d3855029ccc5a Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Mon, 14 Mar 2022 12:08:44 +0100 Subject: [PATCH 25/29] Relation: Split single and multiple relations into different components --- .../{Relation => RelationMultiple}/index.js | 64 ++++++++----------- .../tests/__snapshots__/index.test.js.snap | 2 +- .../tests/index.test.js | 6 +- .../CellContent/RelationSingle/index.js | 32 ++++++++++ .../tests/__snapshots__/index.test.js.snap | 59 +++++++++++++++++ .../RelationSingle/tests/index.test.js | 37 +++++++++++ .../DynamicTable/CellContent/index.js | 15 ++++- 7 files changed, 171 insertions(+), 44 deletions(-) rename packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/{Relation => RelationMultiple}/index.js (71%) rename packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/{Relation => RelationMultiple}/tests/__snapshots__/index.test.js.snap (98%) rename packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/{Relation => RelationMultiple}/tests/index.test.js (92%) create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/index.js create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/tests/__snapshots__/index.test.js.snap create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/tests/index.test.js diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js similarity index 71% rename from packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js rename to packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js index d36ccf51e5..ace7d6a937 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js @@ -14,8 +14,6 @@ import CellValue from '../CellValue'; import { axiosInstance } from '../../../../../core/utils'; import { getRequestUrl, getTrad } from '../../../../utils'; -const SINGLE_RELATIONS = ['oneToOne', 'manyToOne']; - const TypographyMaxWidth = styled(Typography)` max-width: 500px; `; @@ -30,7 +28,7 @@ const fetchRelation = async (endPoint, notifyStatus) => { return { results, pagination }; }; -const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => { +const RelationMultiple = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => { const { formatMessage } = useIntl(); const { notifyStatus } = useNotifyAT(); const requestURL = getRequestUrl(`${queryInfos.endPoint}/${rowId}/${name.split('.')[0]}`); @@ -66,17 +64,6 @@ const Relation = ({ fieldSchema, metadatas, queryInfos, name, rowId, value }) => } ); - if (SINGLE_RELATIONS.includes(fieldSchema.relation)) { - return ( - - - - ); - } - return ( )} - {status === 'success' && - data?.results.map(entry => ( - - - - - - ))} + {status === 'success' && ( + <> + {data?.results.map(entry => ( + + + + + + ))} - {status === 'success' && data?.pagination.total > 10 && ( - - ... - + {data?.pagination.total > 10 && ( + + ... + + )} + )} ); }; -Relation.propTypes = { +RelationMultiple.propTypes = { fieldSchema: PropTypes.shape({ relation: PropTypes.string, targetModel: PropTypes.string, @@ -142,4 +132,4 @@ Relation.propTypes = { value: PropTypes.object.isRequired, }; -export default Relation; +export default RelationMultiple; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/__snapshots__/index.test.js.snap b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/__snapshots__/index.test.js.snap similarity index 98% rename from packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/__snapshots__/index.test.js.snap rename to packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/__snapshots__/index.test.js.snap index 793fddf747..d026fac506 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/__snapshots__/index.test.js.snap +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/__snapshots__/index.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DynamicTabe / Cellcontent / Relation renders and matches the snapshot 1`] = ` +exports[`DynamicTabe / Cellcontent / RelationMultiple renders and matches the snapshot 1`] = ` .c11 { border: 0; -webkit-clip: rect(0 0 0 0); diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/index.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js similarity index 92% rename from packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/index.test.js rename to packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js index 9318b9c486..638c5511c3 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/Relation/tests/index.test.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js @@ -5,7 +5,7 @@ import { IntlProvider } from 'react-intl'; import { QueryClientProvider, QueryClient } from 'react-query'; import { axiosInstance } from '../../../../../../core/utils'; -import Relation from '../index'; +import RelationMultiple from '../index'; jest.spyOn(axiosInstance, 'get').mockResolvedValue({ data: { @@ -53,14 +53,14 @@ const ComponentFixture = () => { - + ); }; -describe('DynamicTabe / Cellcontent / Relation', () => { +describe('DynamicTabe / Cellcontent / RelationMultiple', () => { it('renders and matches the snapshot', async () => { const { container } = render(); expect(container).toMatchSnapshot(); diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/index.js new file mode 100644 index 0000000000..818d82370d --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/index.js @@ -0,0 +1,32 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Typography } from '@strapi/design-system/Typography'; +import styled from 'styled-components'; +import CellValue from '../CellValue'; + +const TypographyMaxWidth = styled(Typography)` + max-width: 500px; +`; + +const RelationSingle = ({ metadatas, value }) => { + return ( + + + + ); +}; + +RelationSingle.propTypes = { + metadatas: PropTypes.shape({ + mainField: PropTypes.shape({ + name: PropTypes.string.isRequired, + schema: PropTypes.shape({ type: PropTypes.string.isRequired }).isRequired, + }), + }).isRequired, + value: PropTypes.object.isRequired, +}; + +export default RelationSingle; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/tests/__snapshots__/index.test.js.snap b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/tests/__snapshots__/index.test.js.snap new file mode 100644 index 0000000000..89b16db8f0 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/tests/__snapshots__/index.test.js.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DynamicTabe / Cellcontent / RelationSingle renders and matches the snapshot 1`] = ` +.c2 { + border: 0; + -webkit-clip: rect(0 0 0 0); + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +.c0 { + color: #32324d; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.875rem; + line-height: 1.43; +} + +.c1 { + max-width: 500px; +} + +
+ + + +
+

+

+

+
+`; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/tests/index.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/tests/index.test.js new file mode 100644 index 0000000000..a923e27a50 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationSingle/tests/index.test.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { ThemeProvider, lightTheme } from '@strapi/design-system'; +import { IntlProvider } from 'react-intl'; + +import RelationSingle from '../index'; + +const DEFAULT_PROPS_FIXTURE = { + metadatas: { + mainField: { + name: 'name', + schema: { + type: 'string', + }, + }, + }, + value: { + count: 1, + }, +}; + +const ComponentFixture = () => { + return ( + + + + + + ); +}; + +describe('DynamicTabe / Cellcontent / RelationSingle', () => { + it('renders and matches the snapshot', async () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js index cd64e6fb3c..5eb2d27a9e 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js @@ -4,7 +4,8 @@ import styled from 'styled-components'; import { Typography } from '@strapi/design-system/Typography'; import Media from './Media'; import MultipleMedias from './MultipleMedias'; -import Relation from './Relation'; +import RelationMultiple from './RelationMultiple'; +import RelationSingle from './RelationSingle'; import RepeatableComponent from './RepeatableComponent'; import SingleComponent from './SingleComponent'; import CellValue from './CellValue'; @@ -29,9 +30,15 @@ const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId return ; - case 'relation': + case 'relation': { + const SINGLE_RELATIONS = ['oneToOne', 'manyToOne']; + + if (SINGLE_RELATIONS.includes(fieldSchema.relation)) { + return ; + } + return ( - ); + } case 'component': if (fieldSchema.repeatable === true) { @@ -69,6 +77,7 @@ CellContent.propTypes = { multiple: PropTypes.bool, type: PropTypes.string.isRequired, repeatable: PropTypes.bool, + relation: PropTypes.string, }).isRequired, metadatas: PropTypes.object.isRequired, name: PropTypes.string.isRequired, From 43cf28ffa6e59c53dcab0ee05d69bb0965e71abd Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Mon, 14 Mar 2022 12:09:41 +0100 Subject: [PATCH 26/29] Relation: Update test copy --- .../CellContent/RelationMultiple/tests/index.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js index 638c5511c3..c0c787bfa8 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js @@ -12,7 +12,7 @@ jest.spyOn(axiosInstance, 'get').mockResolvedValue({ results: [ { id: 1, - name: 'relation entity 1', + name: 'Relation entity 1', }, ], @@ -73,8 +73,8 @@ describe('DynamicTabe / Cellcontent / RelationMultiple', () => { fireEvent(button, new MouseEvent('mousedown', { bubbles: true })); - expect(screen.getByText('The relations are loading')).toBeInTheDocument(); + expect(screen.getByText('Relations are loading')).toBeInTheDocument(); expect(axiosInstance.get).toHaveBeenCalledTimes(1); - await waitFor(() => expect(screen.getByText('relation entity 1')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('Relation entity 1')).toBeInTheDocument()); }); }); From 9acaddb641d5fafc58d68fc5184c98a52ab59979 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Mon, 14 Mar 2022 12:18:04 +0100 Subject: [PATCH 27/29] Relation: Fix displaying single relations --- .../DynamicTable/CellContent/index.js | 4 +-- .../CellContent/utils/hasContent.js | 4 +++ .../utils/tests/hasContent.test.js | 26 ++++++++++++++++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js index 5eb2d27a9e..1f962f78e7 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js @@ -31,9 +31,7 @@ const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId return ; case 'relation': { - const SINGLE_RELATIONS = ['oneToOne', 'manyToOne']; - - if (SINGLE_RELATIONS.includes(fieldSchema.relation)) { + if (['oneToOne', 'manyToOne'].includes(fieldSchema.relation)) { return ; } diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js index 718fe9287b..949b80f699 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js @@ -16,6 +16,10 @@ export default function hasContent(type, content, metadatas, fieldSchema) { } if (type === 'relation') { + if (['oneToOne', 'manyToOne'].includes(fieldSchema.relation)) { + return !isEmpty(content); + } + return content.count > 0; } diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js index ecc032bac0..b3c3bd23ba 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js @@ -74,13 +74,31 @@ describe('hasContent', () => { expect(normalizedContent).toEqual(false); }); - it('extracts content from relations with content', () => { - const normalizedContent = hasContent('relation', { count: 1 }); + it('extracts content from multiple relations with content', () => { + const normalizedContent = hasContent('relation', { count: 1 }, undefined, { + relation: 'manyToMany', + }); expect(normalizedContent).toEqual(true); }); - it('extracts content from relations without content', () => { - const normalizedContent = hasContent('relation', { count: 0 }); + it('extracts content from multiple relations without content', () => { + const normalizedContent = hasContent('relation', { count: 0 }, undefined, { + relation: 'manyToMany', + }); + expect(normalizedContent).toEqual(false); + }); + + it('extracts content from single relations with content', () => { + const normalizedContent = hasContent('relation', { id: 1 }, undefined, { + relation: 'oneToOne', + }); + expect(normalizedContent).toEqual(true); + }); + + it('extracts content from single relations without content', () => { + const normalizedContent = hasContent('relation', null, undefined, { + relation: 'oneToOne', + }); expect(normalizedContent).toEqual(false); }); }); From 949d3f68ebc2d2ba43f25a09e916c5a9bfaed310 Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Tue, 15 Mar 2022 10:43:37 +0100 Subject: [PATCH 28/29] RelationSingle: allow type oneToOneMorph --- .../components/DynamicTable/CellContent/index.js | 3 ++- .../DynamicTable/CellContent/utils/hasContent.js | 4 +++- .../CellContent/utils/isSingleRelation.js | 3 +++ .../utils/tests/isSingleRelation.test.js | 13 +++++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/isSingleRelation.js create mode 100644 packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/isSingleRelation.test.js diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js index 1f962f78e7..4798cf2e99 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/index.js @@ -10,6 +10,7 @@ import RepeatableComponent from './RepeatableComponent'; import SingleComponent from './SingleComponent'; import CellValue from './CellValue'; import hasContent from './utils/hasContent'; +import isSingleRelation from './utils/isSingleRelation'; const TypographyMaxWidth = styled(Typography)` max-width: 300px; @@ -31,7 +32,7 @@ const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId return ; case 'relation': { - if (['oneToOne', 'manyToOne'].includes(fieldSchema.relation)) { + if (isSingleRelation(fieldSchema.relation)) { return ; } diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js index 949b80f699..f2b18ddc4e 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js @@ -1,5 +1,7 @@ import isEmpty from 'lodash/isEmpty'; +import isSingleRelation from './isSingleRelation'; + export default function hasContent(type, content, metadatas, fieldSchema) { if (type === 'component') { const { @@ -16,7 +18,7 @@ export default function hasContent(type, content, metadatas, fieldSchema) { } if (type === 'relation') { - if (['oneToOne', 'manyToOne'].includes(fieldSchema.relation)) { + if (isSingleRelation(fieldSchema.relation)) { return !isEmpty(content); } diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/isSingleRelation.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/isSingleRelation.js new file mode 100644 index 0000000000..542c67eac2 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/isSingleRelation.js @@ -0,0 +1,3 @@ +export default function isSingleRelation(type) { + return ['oneToOne', 'manyToOne', 'oneToOneMorph'].includes(type); +} diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/isSingleRelation.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/isSingleRelation.test.js new file mode 100644 index 0000000000..eb9848be01 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/isSingleRelation.test.js @@ -0,0 +1,13 @@ +import isSingleRelation from '../isSingleRelation'; + +describe('isSingleRelation', () => { + ['oneToOne', 'manyToOne', 'oneToOneMorph'].forEach(type => { + test(`is single relation: ${type}`, () => { + expect(isSingleRelation(type)).toBeTruthy(); + }); + }); + + test('is not single relation', () => { + expect(isSingleRelation('manyToMany')).toBeFalsy(); + }); +}); From e8131df525934a2aaadce678bba2949a53d3388e Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Tue, 15 Mar 2022 11:07:46 +0100 Subject: [PATCH 29/29] CellContent: Add more relation tests --- .../utils/tests/hasContent.test.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js index b3c3bd23ba..0665914ecd 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/utils/tests/hasContent.test.js @@ -101,4 +101,25 @@ describe('hasContent', () => { }); expect(normalizedContent).toEqual(false); }); + + it('returns oneToManyMorph relations as false with content', () => { + const normalizedContent = hasContent('relation', { id: 1 }, undefined, { + relation: 'oneToManyMorph', + }); + expect(normalizedContent).toEqual(false); + }); + + it('extracts content from oneToManyMorph relations with content', () => { + const normalizedContent = hasContent('relation', { id: 1 }, undefined, { + relation: 'oneToOneMorph', + }); + expect(normalizedContent).toEqual(true); + }); + + it('extracts content from oneToManyMorph relations with content', () => { + const normalizedContent = hasContent('relation', null, undefined, { + relation: 'oneToOneMorph', + }); + expect(normalizedContent).toEqual(false); + }); });