From b80847e6782e382a64b61d491a8c07f6fce1dcc5 Mon Sep 17 00:00:00 2001 From: Marc-Roig Date: Wed, 19 Oct 2022 10:21:54 +0200 Subject: [PATCH 01/30] add media attribute validator --- .../lib/services/entity-validator/index.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/core/strapi/lib/services/entity-validator/index.js b/packages/core/strapi/lib/services/entity-validator/index.js index 9473366198..c7df8e0905 100644 --- a/packages/core/strapi/lib/services/entity-validator/index.js +++ b/packages/core/strapi/lib/services/entity-validator/index.js @@ -158,11 +158,27 @@ const createScalarAttributeValidator = (createOrUpdate) => (metas, options) => { return validator; }; +const createMediaAttributeValidator = (createOrUpdate) => (metas, options) => { + let validator; + + if (metas.attr.multiple) { + validator = yup.array().of(yup.mixed()); + } else { + validator = yup.mixed(); + } + + validator = addRequiredValidation(createOrUpdate)(validator, { + attr: { required: !options.isDraft && metas.attr.required }, + }); + + return validator; +}; + const createAttributeValidator = (createOrUpdate) => (metas, options) => { let validator; if (isMediaAttribute(metas.attr)) { - validator = yup.mixed(); + validator = createMediaAttributeValidator(createOrUpdate)(metas, options); } else if (isScalarAttribute(metas.attr)) { validator = createScalarAttributeValidator(createOrUpdate)(metas, options); } else { From 7077008f63989441a8f7f9d5a044d88392aa3e6e Mon Sep 17 00:00:00 2001 From: Marc-Roig Date: Wed, 19 Oct 2022 11:24:15 +0200 Subject: [PATCH 02/30] test for required media attribute --- .../strapi/tests/api/basic-media.test.e2e.js | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 packages/core/strapi/tests/api/basic-media.test.e2e.js diff --git a/packages/core/strapi/tests/api/basic-media.test.e2e.js b/packages/core/strapi/tests/api/basic-media.test.e2e.js new file mode 100644 index 0000000000..67aa0cb91c --- /dev/null +++ b/packages/core/strapi/tests/api/basic-media.test.e2e.js @@ -0,0 +1,119 @@ +'use strict'; + +const { createStrapiInstance } = require('../../../../../test/helpers/strapi'); +const { createTestBuilder } = require('../../../../../test/helpers/builder'); +const { createContentAPIRequest } = require('../../../../../test/helpers/request'); + +const builder = createTestBuilder(); +let strapi; +let rq; + +const productWithMedia = { + attributes: { + name: { + type: 'string', + }, + description: { + type: 'text', + }, + media: { + type: 'media', + multiple: false, + required: true, + allowedTypes: ['images', 'files', 'videos', 'audios'], + }, + multipleMedia: { + type: 'media', + multiple: true, + required: true, + allowedTypes: ['images', 'files', 'videos', 'audios'], + }, + }, + displayName: 'product-with-media', + singularName: 'product-with-media', + pluralName: 'product-with-medias', + description: '', + collectionName: '', +}; + +describe('Core API - Basic + required media', () => { + beforeAll(async () => { + await builder.addContentType(productWithMedia).build(); + + strapi = await createStrapiInstance(); + rq = await createContentAPIRequest({ strapi }); + }); + + afterAll(async () => { + await strapi.destroy(); + await builder.cleanup(); + }); + + test('Create entry without required multiple media', async () => { + const product = { + name: 'product', + description: 'description', + media: 1, + }; + + const res = await rq({ + method: 'POST', + url: '/product-with-medias', + body: { data: product }, + qs: { populate: true }, + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toMatchObject({ + data: null, + error: { + status: 400, + name: 'ValidationError', + message: 'multipleMedia must be defined.', + details: { + errors: [ + { + path: ['multipleMedia'], + message: 'multipleMedia must be defined.', + name: 'ValidationError', + }, + ], + }, + }, + }); + }); + + test('Create entry without required single media', async () => { + const product = { + name: 'product', + description: 'description', + multipleMedia: [{ id: 1 }], + }; + + const res = await rq({ + method: 'POST', + url: '/product-with-medias', + body: { data: product }, + qs: { populate: true }, + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toMatchObject({ + data: null, + error: { + status: 400, + name: 'ValidationError', + message: 'media must be defined.', + details: { + errors: [ + { + path: ['media'], + message: 'media must be defined.', + name: 'ValidationError', + }, + ], + }, + }, + }); + }); +}); From a95811b7b6df676f0cedc0a3981d002021b27a84 Mon Sep 17 00:00:00 2001 From: Fernando Chavez Date: Fri, 17 Feb 2023 15:41:11 +0100 Subject: [PATCH 03/30] Add new cloud box on homepage and update ContentBox component and its documentation --- .../admin/src/pages/HomePage/ContentBlocks.js | 53 ++++ .../assets/strapi-cloud-background.png | Bin 0 -> 3587 bytes .../HomePage/assets/strapi-cloud-flags.svg | 139 +++++++++ .../HomePage/assets/strapi-cloud-icon.svg | 3 + .../src/pages/HomePage/tests/index.test.js | 293 ++++++++++++------ .../tests/__snapshots__/index.test.js.snap | 1 + .../tests/__snapshots__/plugins.test.js.snap | 1 + .../__snapshots__/providers.test.js.snap | 1 + .../tests/__snapshots__/index.test.js.snap | 1 + packages/core/admin/package.json | 2 +- .../ContentBox/ContentBox.stories.mdx | 22 ++ .../lib/src/components/ContentBox/index.js | 36 ++- .../components/ContentBox/tests/index.test.js | 1 + 13 files changed, 443 insertions(+), 110 deletions(-) create mode 100644 packages/core/admin/admin/src/pages/HomePage/assets/strapi-cloud-background.png create mode 100644 packages/core/admin/admin/src/pages/HomePage/assets/strapi-cloud-flags.svg create mode 100644 packages/core/admin/admin/src/pages/HomePage/assets/strapi-cloud-icon.svg diff --git a/packages/core/admin/admin/src/pages/HomePage/ContentBlocks.js b/packages/core/admin/admin/src/pages/HomePage/ContentBlocks.js index 9feb7bb48e..899d0e1c8b 100644 --- a/packages/core/admin/admin/src/pages/HomePage/ContentBlocks.js +++ b/packages/core/admin/admin/src/pages/HomePage/ContentBlocks.js @@ -7,11 +7,37 @@ import InformationSquare from '@strapi/icons/InformationSquare'; import CodeSquare from '@strapi/icons/CodeSquare'; import PlaySquare from '@strapi/icons/PlaySquare'; import FeatherSquare from '@strapi/icons/FeatherSquare'; +import cloudIconBackground from './assets/strapi-cloud-background.png'; +import cloudIcon from './assets/strapi-cloud-icon.svg'; +import cloudFlags from './assets/strapi-cloud-flags.svg'; const BlockLink = styled.a` text-decoration: none; `; +const CloudCustomWrapper = styled.div` + width: 56px; + height: 56px; + background-image: url(${({ src }) => src}); + padding: 11px; + margin-right: ${({ theme }) => theme.spaces[6]}; +`; + +const CloudIconWrapper = styled.div` + background: rgba(255, 255, 255, 0.3); + border-radius: 4px; + display: flex; + justify-content: center; + align-items: center; + height: 100%; +`; + +const CloudFlags = styled.img` + position: absolute; + top: 0; + right: 0; +`; + const ContentBlocks = () => { const { formatMessage } = useIntl(); const { trackUsage } = useTracking(); @@ -22,6 +48,33 @@ const ContentBlocks = () => { return ( + handleClick('didClickOnTryStrapiCloudSection')} + > + + + Strapi Cloud + + + } + decorator={} + /> + + 7Rc8wf^(FAK%?Oe_tA?#oHZAXD@7@s6FI# zHje{6q9lo2I&Fb8(JO>^I_VarQ*d${#(6<R&z2@F!cT=$E057Z1FP{FXF(GW@}eOkHI4^k z)q~R^qw$)~#6Onfy7S4(6^w`=ghy99=+51#6OT67Woe5}=S6N^S&vV7^HoUk-<`OH zp>Q`Md?RtwZR5%nQPnUygW~~3a7^J0nBm)(rehn|GjA-%O(YWaKtE!&KtTb#+Em=w zmkhA2WQiO5{IKK`H`w#a?+eP*owt#?3(cew=m5&9Wf+zLEqtm*X5@o6SV5#1U`+=z zukyKZ&9-7nFe}nf)0VJ7TU0~~SwCk7P4&|o!OS{0_HvFff-pqMIC39^C8FC%RV}mx zj)mhJLEFghpx8=Pm2@z|xwYsS!B}1fR)|l=B9$_Z#kRJ(K05CdY$DpveeZl2!s3Z! z_W;idi^{X8w<>C@D8mp)P&J(b$?s2lXheS@_f&`)_SFOBQNBR&bUtW3&?>>(19h;V zRJ3SoDF}D8oTBLra);`72`D65S=sodf%@=>yLwV2R+JUe#E&m&VJR`e^@lhp1dHeP zNxwuZqlikA=ND3P!$L#egYH35e?8SJnH6oqF^qpz`IwHT;z>B+Rnls3BJ5>kTUxMe zGWYyIm z;CNniNBW^ELuC_%WxhqK+}#eQsaM$FIjEpz7k~WG)uY&G3Jhrhde&P}HfJ6I<$WXI ziW_2O9FOO%IuaW-PhZ-)D*j{>1B|W;4w}3QWg>?bX$T81U}vi2Aab*EaI@g20L9g$ zaJ6kR3NzUupk0;@8m-n03dJmO(Vk6=G>M+~sa^+x(t_bYQHR*DA5}d?#;;WtWdLwuQR&<>)Y3QV zDf^`zvo7pHu^Bc|Q_{h9Oea;DV{oYdbU#x$sWG?Dd;$VBGnwkjqE6wnZ1dLb#P-=g z#L0yxs4PvjY}k==%*oa_)K@_dE2XT@2s#)Lf<$4_d^kzDr@)Nu%(6QOM?=25R#t69 zK50h#o@i%NUPii;SJq=T0)klw1x1GO*0~vs2*<7-4@3`&vgb^}X@suf;8p!;9}5Z- zF&)*?Qs%c<85{WsV-{Gn7xNO4eQ3>4jH(*5mQ>}0uti1Ln<9W{mzqUHaF>Asx)~fX zhz-wiX=EU2T@P97ELH$=e?vHC1K?y_(Dhtb((X#IJdydu89r<%no9NE*{&fsg;Hyk znU&>5?+-UE5wvFLaNST z1t1y)$QD8>v$J{Ty*5@BX5OJBlNHiAr(Di}05JN2y7YDiE6a+nm29K%5K#aCWsNN^ zDnYD7Rxb%giP;$lA9qYJ^dY$xq&0n~ijGyeDKsJw7dsnq(aKX9N~)}TlB!l64>}rY zx6e10N^-T)1F=(_)`r-?I<{@n2pN`Ks)c(%N$FsAR+=aZooi)9?e82-5~Rb-vq@Xa z39wn#t~wXb$ebc_#>N00j*)iSN^DeN9W7baVK}?B8H~JSd-60++N1@_26BdSvB2>* z+PZ!U7oc*<;xz!JpgWPyXVjJHIF|cjA5peyz_qi?q*W14QiOzHKf2b&a!jF?U}HZW z+B#6G5;FfRtae{%y!J}lu8hrlQn5X6vWbrQu5ZhQatW1O>6D?FH}=C2EiNe0=J?CW z@1laHx6kD8iQWtnDY33)oF!$HILtdCg57CpO|dOpek@;Pf> zFF;kG6lDl>C1~D*uHIJEn0~Gl-3a2h35=UCec zDH-`{$WIE6=@?V--25A7=v}xHl>FI3TuZ`>rb&pRS z$BEYAJfyt>2lYJ0Nc0p7Ud1j1hXN#Y-VTC-w)k#B+S$WEQu{#E)lxf>Hk$GhpV6&m z{-vR;O$+o688NEn8UbA>9Y9<;VQ!>n#@g6}i5bLLjH(=Lq&e-FYq(W@W=B)b0fp*l zy5lJ%Js?rst)^RcPodjn1U9ntjYUiHWnHc+MWq059iTqpghxQ!@|x znjE6RnFFU;hnmXEb<;5;H5n8^vDZe5U&fG-1mjSb(UXSA=!D+nz*F0ikLIOxqz_f) zbcGXL83F~GwqrBzI$xOryp9XMR$*zoYOb;-jOoQw^kX+rocl$_S1zyt9?tMvsLr1j z2fl@9m2<7O!h%lDNpCY{3XEE98kUZ!KG`@ja5$sWDX9r{Q&cf;)Q-IN@s7Lz-Q2%0 zGvR(LY%g5DIG*I}qMqBl%Ep=soYz($PHxtr`d*R)%FDJ?E{?W}5tkt%dgfwL67wmp zpc`i#CM}KK}2LQ#g8r8IBb>E(M0f#Lnl4C9U}Eo8~2#zwAeHSUme;InX@?<=* zQvlJANx{;=66s(JbP1Be<|7Bop@VLH>`f4KI6Iu|{T8lCJt^ad!Me_U`G~p-^DxKU zEDwCq(ZRg+F)GRk#*w3C>~wNcRZc1*zlf+v?;q5ixpc%l9ru+a!w))zfsEnoV4v{V za9~mXD-zb|%%7c6Z1!++zFw8HiuZw1=MDSRUC69KW#3PyA~pOUd_g3I;Iv~nZl`{X z-!3)GHXth0OyA_>_((%^($nN^{TsPSCDxZEkDtCexrX|35tp;@}1Zxq-^f8TD1yy4z>*`YW~ zrhw3RLEKD+!R&hI3?gqY(Pj=GFO|V2AUN4Iz2S$F*7DP0(vX%0N6f~9O%8gMF{BO7 z(})=>{EoNAv^R#0W2$uxt+nB4AMdZcv&F-iLa|_d?^DNr9J%%Knu=KuVqrcKUPg|d z!AMDeJ%7?Yb4+Snn>BC-)`nP(MxEP-6A%5tzts8bG|^t2Gp%ol1kvIYsYg2v$WIwg z@S5q+nWwXkN};WzMB%PdQ_^tctI8olAO2_Gu0Q_bm*4MgUx(@uu37x#6@9FW^zxwT$-_#v{`T6H}y7#+>>Sy=C zPwz`Vp_D93&02`H7te*?!PNh)Fsp6<#eV#P-z=mt#UHQxYebuk`<6Nwte8^w> z*R}ZX4frL0{kI3nU*s=-#(#T|_bZ?G{rxq<-+uj@FTZ!c{tq)r2(>(0)Cd3o002ov JPDHLkV1goV&iViV literal 0 HcmV?d00001 diff --git a/packages/core/admin/admin/src/pages/HomePage/assets/strapi-cloud-flags.svg b/packages/core/admin/admin/src/pages/HomePage/assets/strapi-cloud-flags.svg new file mode 100644 index 0000000000..d9807be9ad --- /dev/null +++ b/packages/core/admin/admin/src/pages/HomePage/assets/strapi-cloud-flags.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/core/admin/admin/src/pages/HomePage/assets/strapi-cloud-icon.svg b/packages/core/admin/admin/src/pages/HomePage/assets/strapi-cloud-icon.svg new file mode 100644 index 0000000000..845ebc8cd1 --- /dev/null +++ b/packages/core/admin/admin/src/pages/HomePage/assets/strapi-cloud-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/admin/admin/src/pages/HomePage/tests/index.test.js b/packages/core/admin/admin/src/pages/HomePage/tests/index.test.js index 97d3e68535..17f795e423 100644 --- a/packages/core/admin/admin/src/pages/HomePage/tests/index.test.js +++ b/packages/core/admin/admin/src/pages/HomePage/tests/index.test.js @@ -80,33 +80,38 @@ describe('Homepage', () => { padding: 24px; border-radius: 4px; box-shadow: 0px 1px 4px rgba(33,33,52,0.1); + position: relative; } - .c25 { + .c30 { + width: 60%; + } + + .c33 { background: #f0f0ff; padding: 12px; border-radius: 4px; } - .c31 { + .c35 { background: #fdf4dc; padding: 12px; border-radius: 4px; } - .c32 { + .c36 { background: #eaf5ff; padding: 12px; border-radius: 4px; } - .c33 { + .c37 { background: #f6ecfc; padding: 12px; border-radius: 4px; } - .c35 { + .c39 { background: #ffffff; padding-top: 24px; padding-right: 20px; @@ -116,11 +121,11 @@ describe('Homepage', () => { box-shadow: 0px 1px 4px rgba(33,33,52,0.1); } - .c36 { + .c40 { padding-bottom: 32px; } - .c47 { + .c51 { padding-right: 8px; } @@ -179,20 +184,20 @@ describe('Homepage', () => { color: #32324d; } - .c30 { + .c31 { font-size: 0.875rem; line-height: 1.43; color: #666687; } - .c38 { + .c42 { font-weight: 500; font-size: 1rem; line-height: 1.25; color: #32324d; } - .c41 { + .c45 { font-size: 0.875rem; line-height: 1.43; color: #4945ff; @@ -216,12 +221,12 @@ describe('Homepage', () => { margin-top: 4px; } - .c37 > * { + .c41 > * { margin-top: 0; margin-bottom: 0; } - .c37 > * + * { + .c41 > * + * { margin-top: 12px; } @@ -351,11 +356,11 @@ describe('Homepage', () => { fill: #ffffff; } - .c39 { + .c43 { cursor: pointer; } - .c40 { + .c44 { display: -webkit-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; @@ -370,15 +375,15 @@ describe('Homepage', () => { outline: none; } - .c40 svg path { + .c44 svg path { fill: #4945ff; } - .c40 svg { + .c44 svg { font-size: 0.625rem; } - .c40:after { + .c44:after { -webkit-transition-property: all; transition-property: all; -webkit-transition-duration: 0.2s; @@ -393,11 +398,11 @@ describe('Homepage', () => { border: 2px solid transparent; } - .c40:focus-visible { + .c44:focus-visible { outline: none; } - .c40:focus-visible:after { + .c44:focus-visible:after { border-radius: 8px; content: ''; position: absolute; @@ -408,14 +413,14 @@ describe('Homepage', () => { border: 2px solid #4945ff; } - .c42 { + .c46 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } - .c45 { + .c49 { padding: 10px 16px; background: #4945ff; border: 1px solid #4945ff; @@ -430,7 +435,7 @@ describe('Homepage', () => { text-decoration: none; } - .c45 .c0 { + .c49 .c0 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -441,58 +446,58 @@ describe('Homepage', () => { align-items: center; } - .c45 .c13 { + .c49 .c13 { color: #ffffff; } - .c45[aria-disabled='true'] { + .c49[aria-disabled='true'] { border: 1px solid #dcdce4; background: #eaeaef; } - .c45[aria-disabled='true'] .c13 { + .c49[aria-disabled='true'] .c13 { color: #666687; } - .c45[aria-disabled='true'] svg > g,.c45[aria-disabled='true'] svg path { + .c49[aria-disabled='true'] svg > g,.c49[aria-disabled='true'] svg path { fill: #666687; } - .c45[aria-disabled='true']:active { + .c49[aria-disabled='true']:active { border: 1px solid #dcdce4; background: #eaeaef; } - .c45[aria-disabled='true']:active .c13 { + .c49[aria-disabled='true']:active .c13 { color: #666687; } - .c45[aria-disabled='true']:active svg > g,.c45[aria-disabled='true']:active svg path { + .c49[aria-disabled='true']:active svg > g,.c49[aria-disabled='true']:active svg path { fill: #666687; } - .c45:hover { + .c49:hover { background-color: #f6f6f9; } - .c45:active { + .c49:active { background-color: #eaeaef; } - .c45 .c13 { + .c49 .c13 { color: #32324d; } - .c45 svg > g, - .c45 svg path { + .c49 svg > g, + .c49 svg path { fill: #32324d; } - .c26 { + .c34 { margin-right: 24px; } - .c26 svg { + .c34 svg { width: 2rem; height: 2rem; } @@ -526,12 +531,12 @@ describe('Homepage', () => { max-width: 100%; } - .c34 { + .c38 { grid-column: span 4; max-width: 100%; } - .c44 { + .c48 { grid-column: span 6; max-width: 100%; } @@ -540,55 +545,55 @@ describe('Homepage', () => { outline: none; } - .c48 path { + .c52 path { fill: #7289da !important; } - .c49 > path:first-child { + .c53 > path:first-child { fill: #ff4500; } - .c52 > path:first-child { + .c56 > path:first-child { fill: #4945ff; } - .c52 > path:nth-child(2) { + .c56 > path:nth-child(2) { fill: #fff; } - .c52 > path:nth-child(4) { + .c56 > path:nth-child(4) { fill: #9593ff; } - .c50 path { + .c54 path { fill: #1da1f2 !important; } - .c51 > path:first-child { + .c55 > path:first-child { fill: #231f20; } - .c51 > path:nth-child(2) { + .c55 > path:nth-child(2) { fill: #fff9ae; } - .c51 > path:nth-child(3) { + .c55 > path:nth-child(3) { fill: #00aeef; } - .c51 > path:nth-child(4) { + .c55 > path:nth-child(4) { fill: #00a94f; } - .c51 > path:nth-child(5) { + .c55 > path:nth-child(5) { fill: #f15d22; } - .c51 > path:nth-child(6) { + .c55 > path:nth-child(6) { fill: #e31b23; } - .c46 { + .c50 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -600,16 +605,16 @@ describe('Homepage', () => { border: none; } - .c46 svg { + .c50 svg { width: 24px; height: 24px; } - .c46 span { + .c50 span { word-break: keep-all; } - .c43 { + .c47 { row-gap: 8px; -webkit-column-gap: 16px; column-gap: 16px; @@ -631,6 +636,38 @@ describe('Homepage', () => { text-decoration: none; } + .c25 { + width: 56px; + height: 56px; + background-image: url(IMAGE_MOCK); + padding: 11px; + margin-right: 24px; + } + + .c26 { + background: rgba(255,255,255,0.3); + border-radius: 4px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -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; + height: 100%; + } + + .c32 { + position: absolute; + top: 0; + right: 0; + } + .c5 { position: absolute; top: 0; @@ -654,25 +691,25 @@ describe('Homepage', () => { } @media (max-width:68.75rem) { - .c34 { + .c38 { grid-column: span 12; } } @media (max-width:34.375rem) { - .c34 { + .c38 { grid-column: span; } } @media (max-width:68.75rem) { - .c44 { + .c48 { grid-column: span 12; } } @media (max-width:34.375rem) { - .c44 { + .c48 { grid-column: span; } } @@ -773,6 +810,56 @@ describe('Homepage', () => {