From 69a08228ee93182d4ad80039877c0cc66f74c086 Mon Sep 17 00:00:00 2001 From: Mark Kaylor Date: Wed, 13 Jul 2022 11:19:30 +0200 Subject: [PATCH 01/10] Convert type customField to underlying data type --- .../server/utils/__tests__/attributes.test.js | 34 +++++++++++++++++++ .../server/utils/attributes.js | 13 +++++++ .../lib/core/registries/custom-fields.js | 3 ++ 3 files changed, 50 insertions(+) create mode 100644 packages/core/content-type-builder/server/utils/__tests__/attributes.test.js diff --git a/packages/core/content-type-builder/server/utils/__tests__/attributes.test.js b/packages/core/content-type-builder/server/utils/__tests__/attributes.test.js new file mode 100644 index 0000000000..66525d33ad --- /dev/null +++ b/packages/core/content-type-builder/server/utils/__tests__/attributes.test.js @@ -0,0 +1,34 @@ +'use strict'; + +const { formatAttribute } = require('../attributes'); + +describe('format attributes', () => { + it('replaces type customField with the underlying data type', () => { + const mockAttribute = { + type: 'customField', + customField: 'plugin::mycustomfields.color', + }; + + global.strapi = { + container: { + // mock container.get('custom-fields') + get: jest.fn(() => ({ + // mock container.get('custom-fields').get(uid) + get: jest.fn(() => ({ + name: 'color', + plugin: 'mycustomfields', + type: 'text', + })), + })), + }, + }; + + const formattedAttribute = formatAttribute('key', mockAttribute); + + const expected = { + type: 'text', + customField: 'plugin::mycustomfields.color', + }; + expect(formattedAttribute).toEqual(expected); + }); +}); diff --git a/packages/core/content-type-builder/server/utils/attributes.js b/packages/core/content-type-builder/server/utils/attributes.js index e90fc1fd1a..980108bd11 100644 --- a/packages/core/content-type-builder/server/utils/attributes.js +++ b/packages/core/content-type-builder/server/utils/attributes.js @@ -67,6 +67,19 @@ const formatAttribute = (key, attribute) => { }; } + if (attribute.type === 'customField') { + const customField = strapi.container.get('custom-fields').get(attribute.customField); + + if (!customField) { + throw new Error(`Could not find Custom Field: ${attribute.customField}`); + } + + return { + ...attribute, + type: customField.type, + }; + } + return attribute; }; diff --git a/packages/core/strapi/lib/core/registries/custom-fields.js b/packages/core/strapi/lib/core/registries/custom-fields.js index db850c8773..2970324556 100644 --- a/packages/core/strapi/lib/core/registries/custom-fields.js +++ b/packages/core/strapi/lib/core/registries/custom-fields.js @@ -10,6 +10,9 @@ const customFieldsRegistry = strapi => { getAll() { return customFields; }, + get(customField) { + return customFields[customField]; + }, add(customField) { const customFieldList = Array.isArray(customField) ? customField : [customField]; From a8aabcdeb293fb0318547da7b067905595f136f4 Mon Sep 17 00:00:00 2001 From: Mark Kaylor Date: Wed, 13 Jul 2022 11:19:44 +0200 Subject: [PATCH 02/10] Fix documentation plugin --- .../server/services/helpers/utils/clean-schema-attributes.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/plugins/documentation/server/services/helpers/utils/clean-schema-attributes.js b/packages/plugins/documentation/server/services/helpers/utils/clean-schema-attributes.js index 0c8bc4a467..b3fdc73925 100644 --- a/packages/plugins/documentation/server/services/helpers/utils/clean-schema-attributes.js +++ b/packages/plugins/documentation/server/services/helpers/utils/clean-schema-attributes.js @@ -19,6 +19,11 @@ const cleanSchemaAttributes = (attributes, { typeMap = new Map(), isRequest = fa delete attributesCopy[prop].default; } + if (attribute.type === 'customField') { + const customField = strapi.container.get('custom-fields').get(attribute.customField); + attribute.type = customField.type; + } + switch (attribute.type) { case 'password': { if (!isRequest) { From 0b0dc5c12e9dc1e26cc79538ede4d7de02db0883 Mon Sep 17 00:00:00 2001 From: Mark Kaylor Date: Mon, 18 Jul 2022 10:55:16 +0200 Subject: [PATCH 03/10] Test custom field exists --- .../server/utils/attributes.js | 4 - .../__tests__/custom-fields.test.js | 198 ++++++++++-------- .../lib/core/registries/custom-fields.js | 7 +- 3 files changed, 117 insertions(+), 92 deletions(-) diff --git a/packages/core/content-type-builder/server/utils/attributes.js b/packages/core/content-type-builder/server/utils/attributes.js index 980108bd11..620a537811 100644 --- a/packages/core/content-type-builder/server/utils/attributes.js +++ b/packages/core/content-type-builder/server/utils/attributes.js @@ -70,10 +70,6 @@ const formatAttribute = (key, attribute) => { if (attribute.type === 'customField') { const customField = strapi.container.get('custom-fields').get(attribute.customField); - if (!customField) { - throw new Error(`Could not find Custom Field: ${attribute.customField}`); - } - return { ...attribute, type: customField.type, diff --git a/packages/core/strapi/lib/core/registries/__tests__/custom-fields.test.js b/packages/core/strapi/lib/core/registries/__tests__/custom-fields.test.js index b4330905de..60cab67922 100644 --- a/packages/core/strapi/lib/core/registries/__tests__/custom-fields.test.js +++ b/packages/core/strapi/lib/core/registries/__tests__/custom-fields.test.js @@ -8,99 +8,123 @@ const strapi = { }; describe('Custom fields registry', () => { - it('adds a custom field registered in a plugin', () => { - const mockCF = { - name: 'test', - plugin: 'plugintest', - type: 'text', - }; + describe('add', () => { + it('adds a custom field registered in a plugin', () => { + const mockCF = { + name: 'test', + plugin: 'plugintest', + type: 'text', + }; - const customFields = customFieldsRegistry(strapi); - customFields.add(mockCF); + const customFields = customFieldsRegistry(strapi); + customFields.add(mockCF); - const expected = { - 'plugin::plugintest.test': mockCF, - }; - expect(customFields.getAll()).toEqual(expected); + const expected = { + 'plugin::plugintest.test': mockCF, + }; + expect(customFields.getAll()).toEqual(expected); + }); + + it('adds a custom field not registered in a plugin', () => { + const mockCF = { + name: 'test', + type: 'text', + }; + + const customFields = customFieldsRegistry(strapi); + customFields.add(mockCF); + + const expected = { + 'global::test': mockCF, + }; + expect(customFields.getAll()).toEqual(expected); + }); + + it('requires a name key on the custom field', () => { + const mockCF = { + type: 'test', + }; + + const customFields = customFieldsRegistry(strapi); + + expect(() => customFields.add(mockCF)).toThrowError( + `Custom fields require a 'name' and 'type' key` + ); + }); + + it('requires a type key on the custom field', () => { + const mockCF = { + name: 'test', + }; + + const customFields = customFieldsRegistry(strapi); + + expect(() => customFields.add(mockCF)).toThrowError( + `Custom fields require a 'name' and 'type' key` + ); + }); + + it('validates the name can be used as an object key', () => { + const mockCF = { + name: 'test.boom', + type: 'text', + }; + + const customFields = customFieldsRegistry(strapi); + + expect(() => customFields.add(mockCF)).toThrowError( + `Custom field name: 'test.boom' is not a valid object key` + ); + }); + + it('validates the type is a Strapi type', () => { + const mockCF = { + name: 'test', + type: 'geojson', + }; + + const customFields = customFieldsRegistry(strapi); + + expect(() => customFields.add(mockCF)).toThrowError( + `Custom field type: 'geojson' is not a valid Strapi type` + ); + }); + + it('confirms the custom field does not already exist', () => { + const mockCF = { + name: 'test', + plugin: 'plugintest', + type: 'text', + }; + + const customFields = customFieldsRegistry(strapi); + + customFields.add(mockCF); + expect(() => customFields.add(mockCF)).toThrowError( + `Custom field: 'plugin::plugintest.test' has already been registered` + ); + }); }); + describe('get', () => { + it('gets a registered custom field', () => { + const mockCF = { + name: 'test', + plugin: 'plugintest', + type: 'text', + }; - it('adds a custom field not registered in a plugin', () => { - const mockCF = { - name: 'test', - type: 'text', - }; + const customFields = customFieldsRegistry(strapi); + customFields.add(mockCF); - const customFields = customFieldsRegistry(strapi); - customFields.add(mockCF); + expect(customFields.get('plugin::plugintest.test')).toEqual(mockCF); + }); - const expected = { - 'global::test': mockCF, - }; - expect(customFields.getAll()).toEqual(expected); - }); + it('throws when a custom field is not registered', () => { + const customFields = customFieldsRegistry(strapi); - it('requires a name key on the custom field', () => { - const mockCF = { - type: 'test', - }; - - const customFields = customFieldsRegistry(strapi); - - expect(() => customFields.add(mockCF)).toThrowError( - `Custom fields require a 'name' and 'type' key` - ); - }); - - it('requires a type key on the custom field', () => { - const mockCF = { - name: 'test', - }; - - const customFields = customFieldsRegistry(strapi); - - expect(() => customFields.add(mockCF)).toThrowError( - `Custom fields require a 'name' and 'type' key` - ); - }); - - it('validates the name can be used as an object key', () => { - const mockCF = { - name: 'test.boom', - type: 'text', - }; - - const customFields = customFieldsRegistry(strapi); - - expect(() => customFields.add(mockCF)).toThrowError( - `Custom field name: 'test.boom' is not a valid object key` - ); - }); - - it('validates the type is a Strapi type', () => { - const mockCF = { - name: 'test', - type: 'geojson', - }; - - const customFields = customFieldsRegistry(strapi); - - expect(() => customFields.add(mockCF)).toThrowError( - `Custom field type: 'geojson' is not a valid Strapi type` - ); - }); - - it('confirms the custom field does not already exist', () => { - const mockCF = { - name: 'test', - plugin: 'plugintest', - type: 'text', - }; - - const customFields = customFieldsRegistry(strapi); - - customFields.add(mockCF); - expect(() => customFields.add(mockCF)).toThrowError( - `Custom field: 'plugin::plugintest.test' has already been registered` - ); + expect(() => customFields.get('plugin::plugintest.test')).toThrowError( + `Could not find Custom Field: plugin::plugintest.test` + ); + }); }); }); diff --git a/packages/core/strapi/lib/core/registries/custom-fields.js b/packages/core/strapi/lib/core/registries/custom-fields.js index 2970324556..f254c3f961 100644 --- a/packages/core/strapi/lib/core/registries/custom-fields.js +++ b/packages/core/strapi/lib/core/registries/custom-fields.js @@ -11,7 +11,12 @@ const customFieldsRegistry = strapi => { return customFields; }, get(customField) { - return customFields[customField]; + const registeredCustomField = customFields[customField]; + if (!registeredCustomField) { + throw new Error(`Could not find Custom Field: ${customField}`); + } + + return registeredCustomField; }, add(customField) { const customFieldList = Array.isArray(customField) ? customField : [customField]; From ded30b56e3dbd7e579d10ae02a520c4430b4cbb6 Mon Sep 17 00:00:00 2001 From: Mark Kaylor Date: Mon, 18 Jul 2022 14:32:25 +0200 Subject: [PATCH 04/10] Add tabs to attribute modal --- .../src/components/AttributeOptions/index.js | 93 +- .../tests/__snapshots__/index.test.js.snap | 1232 +++++++++++++++++ .../AttributeOptions/tests/index.test.js | 105 ++ .../admin/src/components/FormModal/index.js | 10 +- .../admin/src/translations/en.json | 2 + 5 files changed, 1389 insertions(+), 53 deletions(-) create mode 100644 packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap create mode 100644 packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/index.test.js diff --git a/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js b/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js index db130b6eb0..711a13cb06 100644 --- a/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js +++ b/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js @@ -12,66 +12,71 @@ import { Divider } from '@strapi/design-system/Divider'; import { Grid, GridItem } from '@strapi/design-system/Grid'; import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable'; import { ModalBody } from '@strapi/design-system/ModalLayout'; -import { Flex } from '@strapi/design-system/Flex'; import { Stack } from '@strapi/design-system/Stack'; -import { Typography } from '@strapi/design-system/Typography'; +import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/Tabs'; import { getTrad } from '../../utils'; import AttributeOption from './AttributeOption'; -const AttributeOptions = ({ attributes, forTarget, kind }) => { +const AttributeOptions = ({ attributes }) => { const { formatMessage } = useIntl(); - const titleIdSuffix = forTarget.includes('component') ? 'component' : kind; - const titleId = getTrad(`modalForm.sub-header.chooseAttribute.${titleIdSuffix}`); + const defaultTabId = getTrad(`modalForm.tabs.default`); + const customTabId = getTrad(`modalForm.tabs.custom`); return ( - - - - {formatMessage({ id: titleId, defaultMessage: 'Select a field' })} - - - - - - - {attributes.map((attributeRow, index) => { - const key = index; + + + + {formatMessage({ id: defaultTabId, defaultMessage: 'Default' })} + {formatMessage({ id: customTabId, defaultMessage: 'Custom' })} + + + + + + + + + {attributes.map((attributeRow, index) => { + const key = index; - return ( - - {attributeRow.map((attribute, index) => { - const isOdd = index % 2 === 1; - const paddingLeft = isOdd ? 2 : 0; - const paddingRight = isOdd ? 0 : 2; + return ( + + {attributeRow.map((attribute, index) => { + const isOdd = index % 2 === 1; + const paddingLeft = isOdd ? 2 : 0; + const paddingRight = isOdd ? 0 : 2; - return ( - - - - - - ); - })} - - ); - })} - - - + return ( + + + + + + ); + })} + + ); + })} + + + + + Coming soon + + + ); }; AttributeOptions.propTypes = { attributes: PropTypes.array.isRequired, - forTarget: PropTypes.string.isRequired, - kind: PropTypes.string.isRequired, }; export default AttributeOptions; diff --git a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap new file mode 100644 index 0000000000..1853dfc30a --- /dev/null +++ b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap @@ -0,0 +1,1232 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AttributeOptions renders and matches the snapshot 1`] = ` +.c24 { + 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; +} + +.c8 { + padding-bottom: 24px; +} + +.c15 { + padding-right: 8px; + padding-bottom: 8px; + padding-left: 0px; +} + +.c16 { + padding: 16px; + border-radius: 4px; +} + +.c20 { + padding-left: 16px; +} + +.c23 { + padding-right: 0px; + padding-bottom: 8px; + padding-left: 8px; +} + +.c18 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; +} + +.c11 { + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; +} + +.c12 > * { + margin-top: 0; + margin-bottom: 0; +} + +.c12 > * + * { + margin-top: 40px; +} + +.c21 { + font-weight: 600; + color: #32324d; + font-size: 0.875rem; + line-height: 1.43; +} + +.c22 { + color: #666687; + font-size: 0.75rem; + line-height: 1.33; +} + +.c9 { + background: #eaeaef; +} + +.c10 { + height: 1px; + border: none; + margin: 0; +} + +.c13 { + display: grid; + grid-template-columns: repeat(12,1fr); + gap: 0px; +} + +.c14 { + grid-column: span 6; + max-width: 100%; +} + +.c0 { + padding: 12px; + padding-right: 24px; + padding-left: 24px; +} + +.c1 { + overflow: auto; + max-height: 60vh; +} + +.c5 { + color: #4945ff; + font-weight: 600; + font-size: 0.6875rem; + line-height: 1.45; + text-transform: uppercase; +} + +.c7 { + color: #666687; + font-weight: 600; + font-size: 0.6875rem; + line-height: 1.45; + text-transform: uppercase; +} + +.c3 { + padding: 16px; +} + +.c4 { + border-bottom: 2px solid #4945ff; +} + +.c6 { + border-bottom: 2px solid transparent; +} + +.c2[aria-disabled='true'] { + cursor: not-allowed; +} + +.c19 { + width: 2rem; + height: 1.5rem; + box-sizing: content-box; +} + +.c17 { + width: 100%; + height: 100%; + border: 1px solid #dcdce4; + text-align: left; +} + +.c17:hover { + background: #f0f0ff; + border: 1px solid #d9d8ff; +} + +@media (max-width:68.75rem) { + .c14 { + grid-column: span; + } +} + +@media (max-width:34.375rem) { + .c14 { + grid-column: span; + } +} + +
+
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+

+

+

+
+`; diff --git a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/index.test.js b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/index.test.js new file mode 100644 index 0000000000..ba786a7820 --- /dev/null +++ b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/index.test.js @@ -0,0 +1,105 @@ +import { render, screen, getByText, fireEvent } from '@testing-library/react'; +import { createMemoryHistory } from 'history'; +import React from 'react'; +import { Router } from 'react-router-dom'; +import { lightTheme, darkTheme } from '@strapi/design-system'; +import LanguageProvider from '../../../../../../admin/admin/src/components/LanguageProvider'; +import Theme from '../../../../../../admin/admin/src/components/Theme'; +import ThemeToggleProvider from '../../../../../../admin/admin/src/components/ThemeToggleProvider'; +import en from '../../../../../../admin/admin/src/translations/en.json'; +import FormModalNavigationProvider from '../../FormModalNavigationProvider'; +import pluginEn from '../../../translations/en.json'; +import getTrad from '../../../utils/getTrad'; +import AttributeOptions from '../index'; + +const mockAttributes = [ + [ + 'text', + 'email', + 'richtext', + 'password', + 'number', + 'enumeration', + 'date', + 'media', + 'boolean', + 'json', + 'relation', + 'uid', + ], + ['component', 'dynamiczone'], +]; + +const makeApp = () => { + const history = createMemoryHistory(); + const messages = { + en: Object.keys(pluginEn).reduce( + (acc, current) => { + acc[getTrad(current)] = pluginEn[current]; + + return acc; + }, + { ...en } + ), + }; + + const localeNames = { en: 'English' }; + + return ( + + + + + + + + + + + + ); +}; + +describe('AttributeOptions', () => { + it('renders and matches the snapshot', () => { + const App = makeApp(); + const { container } = render(App); + + expect(container).toMatchSnapshot(); + }); + + it('shows the simple tabs', async () => { + const App = makeApp(); + render(App); + + const tabs = screen.getByLabelText('Attribute type tabs'); + const defaultTab = await getByText(tabs, 'Default'); + const customTab = await getByText(tabs, 'Custom'); + + expect(defaultTab).toBeVisible(); + expect(customTab).toBeVisible(); + }); + + it('defaults to the default tab', async () => { + const App = makeApp(); + render(App); + + const comingSoonText = screen.queryByText('Coming soon'); + + expect(comingSoonText).toEqual(null); + }); + + it('switches to the custom tab', async () => { + const App = makeApp(); + render(App); + + const customTab = screen.getByRole('tab', { selected: false }); + fireEvent.click(customTab); + const button = screen.getByRole('tab', { selected: true }); + const customTabActive = await getByText(button, 'Custom'); + const comingSoonText = screen.getByText('Coming soon'); + + expect(customTabActive).not.toBe(null); + expect(comingSoonText).toBeVisible(); + }); +}); diff --git a/packages/core/content-type-builder/admin/src/components/FormModal/index.js b/packages/core/content-type-builder/admin/src/components/FormModal/index.js index 6a3f1567d5..67056a2f6d 100644 --- a/packages/core/content-type-builder/admin/src/components/FormModal/index.js +++ b/packages/core/content-type-builder/admin/src/components/FormModal/index.js @@ -891,8 +891,6 @@ const FormModal = () => { advancedFormInputNames.includes(key) ); - const schemaKind = get(contentTypes, [targetUid, 'schema', 'kind']); - return ( <> @@ -907,13 +905,7 @@ const FormModal = () => { targetUid={targetUid} attributeType={attributeType} /> - {isPickingAttribute && ( - - )} + {isPickingAttribute && } {!isPickingAttribute && (
diff --git a/packages/core/content-type-builder/admin/src/translations/en.json b/packages/core/content-type-builder/admin/src/translations/en.json index 77d02a63f2..901cfaec72 100644 --- a/packages/core/content-type-builder/admin/src/translations/en.json +++ b/packages/core/content-type-builder/admin/src/translations/en.json @@ -155,6 +155,8 @@ "modalForm.sub-header.chooseAttribute.collectionType": "Select a field for your collection type", "modalForm.sub-header.chooseAttribute.component": "Select a field for your component", "modalForm.sub-header.chooseAttribute.singleType": "Select a field for your single type", + "modalForm.tabs.default": "Default", + "modalForm.tabs.custom": "Custom", "modelPage.attribute.relation-polymorphic": "Relation (polymorphic)", "modelPage.attribute.relationWith": "Relation with", "notification.error.dynamiczone-min.validation": "At least one component is required in a dynamic zone to be able to save a content type", From cf375116692ca082a872f0bd12306a8deb652b6d Mon Sep 17 00:00:00 2001 From: Mark Kaylor Date: Mon, 18 Jul 2022 16:04:17 +0200 Subject: [PATCH 05/10] Update snapshot --- .../AttributeOptions/tests/__snapshots__/index.test.js.snap | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap index 1853dfc30a..f2a3017eae 100644 --- a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap +++ b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap @@ -110,8 +110,10 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` } .c0 { - padding: 12px; + padding: 32px; + padding-top: 12px; padding-right: 24px; + padding-bottom: 16px; padding-left: 24px; } From 7e8456a92b73cfb568bdc42be7026e2fcea73d2c Mon Sep 17 00:00:00 2001 From: Mark Kaylor Date: Mon, 18 Jul 2022 16:48:43 +0200 Subject: [PATCH 06/10] Revert changes based on deprecated designs --- .../src/components/AttributeOptions/index.js | 28 +- .../tests/__snapshots__/index.test.js.snap | 464 ++++++++++-------- .../AttributeOptions/tests/index.test.js | 6 +- .../admin/src/components/FormModal/index.js | 10 +- 4 files changed, 281 insertions(+), 227 deletions(-) diff --git a/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js b/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js index 711a13cb06..920f3fa5e9 100644 --- a/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js +++ b/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js @@ -13,23 +13,33 @@ import { Grid, GridItem } from '@strapi/design-system/Grid'; import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable'; import { ModalBody } from '@strapi/design-system/ModalLayout'; import { Stack } from '@strapi/design-system/Stack'; +import { Flex } from '@strapi/design-system/Flex'; +import { Typography } from '@strapi/design-system/Typography'; import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/Tabs'; import { getTrad } from '../../utils'; import AttributeOption from './AttributeOption'; -const AttributeOptions = ({ attributes }) => { +const AttributeOptions = ({ attributes, forTarget, kind }) => { const { formatMessage } = useIntl(); const defaultTabId = getTrad(`modalForm.tabs.default`); const customTabId = getTrad(`modalForm.tabs.custom`); + const titleIdSuffix = forTarget.includes('component') ? 'component' : kind; + const titleId = getTrad(`modalForm.sub-header.chooseAttribute.${titleIdSuffix}`); + return ( - + - - {formatMessage({ id: defaultTabId, defaultMessage: 'Default' })} - {formatMessage({ id: customTabId, defaultMessage: 'Custom' })} - + + + {formatMessage({ id: titleId, defaultMessage: 'Select a field' })} + + + {formatMessage({ id: defaultTabId, defaultMessage: 'Default' })} + {formatMessage({ id: customTabId, defaultMessage: 'Custom' })} + + @@ -52,7 +62,7 @@ const AttributeOptions = ({ attributes }) => { @@ -67,7 +77,7 @@ const AttributeOptions = ({ attributes }) => { - Coming soon + Coming soon @@ -77,6 +87,8 @@ const AttributeOptions = ({ attributes }) => { AttributeOptions.propTypes = { attributes: PropTypes.array.isRequired, + forTarget: PropTypes.string.isRequired, + kind: PropTypes.string.isRequired, }; export default AttributeOptions; diff --git a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap index f2a3017eae..88264ac545 100644 --- a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap +++ b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`AttributeOptions renders and matches the snapshot 1`] = ` -.c24 { +.c26 { border: 0; -webkit-clip: rect(0 0 0 0); clip: rect(0 0 0 0); @@ -13,32 +13,50 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` width: 1px; } -.c8 { +.c10 { padding-bottom: 24px; } -.c15 { +.c17 { padding-right: 8px; - padding-bottom: 8px; + padding-bottom: 4px; padding-left: 0px; } -.c16 { +.c18 { padding: 16px; border-radius: 4px; } -.c20 { +.c22 { padding-left: 16px; } -.c23 { +.c25 { padding-right: 0px; - padding-bottom: 8px; + padding-bottom: 4px; padding-left: 8px; } -.c18 { +.c2 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.c20 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -52,7 +70,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` flex-direction: row; } -.c11 { +.c13 { -webkit-align-items: stretch; -webkit-box-align: stretch; -ms-flex-align: stretch; @@ -66,55 +84,58 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` flex-direction: column; } -.c12 > * { +.c14 > * { margin-top: 0; margin-bottom: 0; } -.c12 > * + * { +.c14 > * + * { margin-top: 40px; } -.c21 { +.c3 { + color: #32324d; + font-weight: 600; + font-size: 1.125rem; + line-height: 1.22; +} + +.c23 { font-weight: 600; color: #32324d; font-size: 0.875rem; line-height: 1.43; } -.c22 { +.c24 { color: #666687; font-size: 0.75rem; line-height: 1.33; } -.c9 { +.c11 { background: #eaeaef; } -.c10 { +.c12 { height: 1px; border: none; margin: 0; } -.c13 { +.c15 { display: grid; grid-template-columns: repeat(12,1fr); gap: 0px; } -.c14 { +.c16 { grid-column: span 6; max-width: 100%; } .c0 { - padding: 32px; - padding-top: 12px; - padding-right: 24px; - padding-bottom: 16px; - padding-left: 24px; + padding: 24px; } .c1 { @@ -122,7 +143,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` max-height: 60vh; } -.c5 { +.c7 { color: #4945ff; font-weight: 600; font-size: 0.6875rem; @@ -130,7 +151,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` text-transform: uppercase; } -.c7 { +.c9 { color: #666687; font-weight: 600; font-size: 0.6875rem; @@ -138,48 +159,48 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` text-transform: uppercase; } -.c3 { +.c5 { padding: 16px; } -.c4 { +.c6 { border-bottom: 2px solid #4945ff; } -.c6 { +.c8 { border-bottom: 2px solid transparent; } -.c2[aria-disabled='true'] { +.c4[aria-disabled='true'] { cursor: not-allowed; } -.c19 { +.c21 { width: 2rem; height: 1.5rem; box-sizing: content-box; } -.c17 { +.c19 { width: 100%; height: 100%; border: 1px solid #dcdce4; text-align: left; } -.c17:hover { +.c19:hover { background: #f0f0ff; border: 1px solid #d9d8ff; } @media (max-width:68.75rem) { - .c14 { + .c16 { grid-column: span; } } @media (max-width:34.375rem) { - .c14 { + .c16 { grid-column: span; } } @@ -190,54 +211,63 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` >
- - + + Default + +
+ + +

@@ -251,32 +281,32 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` class="" >
@@ -472,7 +472,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Rich text + richtext
- A rich text editor with formatting options + A type for modeling data
@@ -535,7 +535,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Password + password
- Password field with encryption + A type for modeling data
@@ -602,7 +602,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Number + number
- Numbers (integer, float, decimal) + A type for modeling data
@@ -671,7 +671,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Enumeration + enumeration
- List of values, then pick one + A type for modeling data
@@ -736,7 +736,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Date + date
- A date picker with hours, minutes and seconds + A type for modeling data
@@ -805,7 +805,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Media + media
- Files like images, videos, etc + A type for modeling data
@@ -872,7 +872,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Boolean + boolean
- Yes or no, 1 or 0, true or false + A type for modeling data
@@ -941,7 +941,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - JSON + json
- Data in JSON format + A type for modeling data
@@ -1006,7 +1006,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Relation + relation
- Refers to a Collection Type + A type for modeling data
@@ -1069,7 +1069,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - UID + uid
- Unique identifier + A type for modeling data
@@ -1146,7 +1146,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Component + component
- Group of fields that you can repeat or reuse + A type for modeling data
@@ -1213,7 +1213,7 @@ exports[`AttributeOptions renders and matches the snapshot 1`] = ` - Dynamic zone + dynamiczone
- Dynamically pick component when editing content + A type for modeling data
diff --git a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/index.test.js b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/index.test.js index d24d6865d2..8b927ce234 100644 --- a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/index.test.js +++ b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/index.test.js @@ -1,15 +1,8 @@ -import { render, screen, getByText, fireEvent } from '@testing-library/react'; -import { createMemoryHistory } from 'history'; import React from 'react'; -import { Router } from 'react-router-dom'; -import { lightTheme, darkTheme } from '@strapi/design-system'; -import LanguageProvider from '../../../../../../admin/admin/src/components/LanguageProvider'; -import Theme from '../../../../../../admin/admin/src/components/Theme'; -import ThemeToggleProvider from '../../../../../../admin/admin/src/components/ThemeToggleProvider'; -import en from '../../../../../../admin/admin/src/translations/en.json'; +import { render, screen, getByText, fireEvent } from '@testing-library/react'; +import { lightTheme, ThemeProvider } from '@strapi/design-system'; +import { IntlProvider } from 'react-intl'; import FormModalNavigationProvider from '../../FormModalNavigationProvider'; -import pluginEn from '../../../translations/en.json'; -import getTrad from '../../../utils/getTrad'; import AttributeOptions from '../index'; const mockAttributes = [ @@ -30,62 +23,38 @@ const mockAttributes = [ ['component', 'dynamiczone'], ]; -const makeApp = () => { - const history = createMemoryHistory(); - const messages = { - en: Object.keys(pluginEn).reduce( - (acc, current) => { - acc[getTrad(current)] = pluginEn[current]; +const App = ( + + + + + + + +); - return acc; - }, - { ...en } - ), - }; - - const localeNames = { en: 'English' }; - - return ( - - - - - - - - - - - - ); -}; - -describe('AttributeOptions', () => { +describe('', () => { it('renders and matches the snapshot', () => { - const App = makeApp(); const { container } = render(App); expect(container).toMatchSnapshot(); }); - it('shows the simple tabs', async () => { - const App = makeApp(); + it('shows the simple tabs', () => { render(App); - const tabs = screen.getByLabelText('Attribute type tabs'); - const defaultTab = await getByText(tabs, 'Default'); - const customTab = await getByText(tabs, 'Custom'); + const defaultTab = screen.getByRole('tab', { selected: true }); + const customTab = screen.getByRole('tab', { selected: false }); expect(defaultTab).toBeVisible(); expect(customTab).toBeVisible(); }); - it('defaults to the default tab', async () => { - const App = makeApp(); + it('defaults to the default tab', () => { render(App); const comingSoonText = screen.queryByText('Coming soon'); @@ -93,17 +62,16 @@ describe('AttributeOptions', () => { expect(comingSoonText).toEqual(null); }); - it('switches to the custom tab', async () => { - const App = makeApp(); + it('switches to the custom tab', () => { render(App); const customTab = screen.getByRole('tab', { selected: false }); fireEvent.click(customTab); - const button = screen.getByRole('tab', { selected: true }); - const customTabActive = await getByText(button, 'Custom'); + const customTabSelected = screen.getByRole('tab', { selected: true }); + const customTabText = getByText(customTabSelected, 'Custom'); const comingSoonText = screen.getByText('Coming soon'); - expect(customTabActive).not.toBe(null); + expect(customTabText).not.toBe(null); expect(comingSoonText).toBeVisible(); }); }); From dd37af1315f00373b6ac75afa45978b931483091 Mon Sep 17 00:00:00 2001 From: Mark Kaylor Date: Tue, 19 Jul 2022 12:10:53 +0200 Subject: [PATCH 10/10] Use translation for tab group label --- .../admin/src/components/AttributeOptions/index.js | 9 ++++++++- .../tests/__snapshots__/index.test.js.snap | 2 +- .../content-type-builder/admin/src/translations/en.json | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js b/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js index 7ec1fcddaf..37030fd8d0 100644 --- a/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js +++ b/packages/core/content-type-builder/admin/src/components/AttributeOptions/index.js @@ -30,7 +30,14 @@ const AttributeOptions = ({ attributes, forTarget, kind }) => { return ( - + {formatMessage({ id: titleId, defaultMessage: 'Select a field' })} diff --git a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap index d2f972406b..a1fe23434d 100644 --- a/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap +++ b/packages/core/content-type-builder/admin/src/components/AttributeOptions/tests/__snapshots__/index.test.js.snap @@ -219,7 +219,7 @@ exports[` renders and matches the snapshot 1`] = ` Select a field