From 00743e34fc7cce3042d79d710cddf7f32c6fad1d Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Tue, 16 Apr 2024 11:04:49 +0200 Subject: [PATCH 01/26] feat(SLB-287): create BlockBackgroundImageCards component and stories --- .../BlockBackgroundImageCards.stories.tsx | 36 ++++++++++++ .../PageContent/BlockBackgroundImageCards.tsx | 56 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.stories.tsx create mode 100644 packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx diff --git a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.stories.tsx b/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.stories.tsx new file mode 100644 index 000000000..316056a66 --- /dev/null +++ b/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.stories.tsx @@ -0,0 +1,36 @@ +import { Url } from '@custom/schema'; +import Landscape from '@stories/landscape.jpg?as=metadata'; +import Portrait from '@stories/portrait.jpg?as=metadata'; +import { Meta, StoryObj } from '@storybook/react'; + +import { image } from '../../../helpers/image'; +import { BlockBackgroundImageCards } from './BlockBackgroundImageCards'; + +export default { + component: BlockBackgroundImageCards, +} satisfies Meta; + +export const Default = { + args: { + teasers: [ + { + title: 'Title', + ctaText: 'Call to action', + ctaUrl: '/test' as Url, + image: { + source: image(Landscape), + alt: 'Alt text', + }, + }, + { + title: 'Title', + ctaText: 'Call to action', + ctaUrl: '/test' as Url, + image: { + source: image(Portrait), + alt: 'Alt text', + }, + }, + ], + }, +} satisfies StoryObj; diff --git a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx b/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx new file mode 100644 index 000000000..b8133f8d0 --- /dev/null +++ b/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx @@ -0,0 +1,56 @@ +import { + BlockImageTeaser, + BlockImageTeasers, + Image, + Link, +} from '@custom/schema'; +import React from 'react'; + +import { isTruthy } from '../../../utils/isTruthy'; + +export function BlockBackgroundImageCards(props: BlockImageTeasers) { + return ( + // eslint-disable-next-line tailwindcss/no-custom-classname +
+
+
+ {props.teasers.filter(isTruthy).map((teaser, index) => ( + + ))} +
+
+
+ ); +} + +export function BlockBackgroundImageCard(props: BlockImageTeaser) { + return ( +
+ {props.image ? ( + {props.image.alt} + ) : null} + +
+ {props.title ? ( +

+ {props.title} +

+ ) : null} + + {props.ctaUrl && props.ctaText ? ( + + {props.ctaText} + + ) : null} +
+
+ ); +} From 0db95250010de3db394eb8fa68a4db81bd8f5ce7 Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Mon, 29 Apr 2024 18:25:56 +0200 Subject: [PATCH 02/26] feat(SLB-287): add custom class to teaser container --- .../Organisms/PageContent/BlockBackgroundImageCards.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx b/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx index b8133f8d0..55783c56a 100644 --- a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx +++ b/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx @@ -11,8 +11,8 @@ import { isTruthy } from '../../../utils/isTruthy'; export function BlockBackgroundImageCards(props: BlockImageTeasers) { return ( // eslint-disable-next-line tailwindcss/no-custom-classname -
-
+
+
{props.teasers.filter(isTruthy).map((teaser, index) => ( @@ -25,10 +25,10 @@ export function BlockBackgroundImageCards(props: BlockImageTeasers) { export function BlockBackgroundImageCard(props: BlockImageTeaser) { return ( -
+
{props.image ? ( {props.image.alt} From d51c6b34848878ef604a563267c1fed1c1dbbde7 Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Mon, 29 Apr 2024 18:27:14 +0200 Subject: [PATCH 03/26] feat(SLB-287): create style to remove margin top on default hero --- packages/ui/src/components/Organisms/PageHero.tsx | 2 +- packages/ui/src/tailwind.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/components/Organisms/PageHero.tsx b/packages/ui/src/components/Organisms/PageHero.tsx index 55a1088ca..5e96cfcee 100644 --- a/packages/ui/src/components/Organisms/PageHero.tsx +++ b/packages/ui/src/components/Organisms/PageHero.tsx @@ -15,7 +15,7 @@ export function PageHero(props: NonNullable) { function DefaultHero(props: NonNullable) { return ( -
+
{props.image ? ( {props.image.alt} :first-child.block-background-image-cards { + @apply mt-2 px-0; +} \ No newline at end of file From f06d8f87c2b8744c8a115e1950d7783ae82ab5db Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Mon, 29 Apr 2024 18:27:40 +0200 Subject: [PATCH 04/26] feat(SLB-287): add component to the page and add stories --- .../src/components/Organisms/PageDisplay.tsx | 86 ++++++++----------- .../ui/src/components/Routes/Page.stories.tsx | 9 ++ 2 files changed, 44 insertions(+), 51 deletions(-) diff --git a/packages/ui/src/components/Organisms/PageDisplay.tsx b/packages/ui/src/components/Organisms/PageDisplay.tsx index 86813de03..6eb63aca2 100644 --- a/packages/ui/src/components/Organisms/PageDisplay.tsx +++ b/packages/ui/src/components/Organisms/PageDisplay.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { isTruthy } from '../../utils/isTruthy'; import { UnreachableCaseError } from '../../utils/unreachable-case-error'; import { PageTransition } from '../Molecules/PageTransition'; +import { BlockBackgroundImageCards } from './PageContent/BlockBackgroundImageCards'; import { BlockCta } from './PageContent/BlockCta'; import { BlockForm } from './PageContent/BlockForm'; import { BlockMarkup } from './PageContent/BlockMarkup'; @@ -14,58 +15,41 @@ import { PageHero } from './PageHero'; export function PageDisplay(page: PageFragment) { return ( + {page.hero ? : null}
- {page.hero ? : null} -
-
- {page?.content?.filter(isTruthy).map((block, index) => { - switch (block.__typename) { - case 'BlockMedia': - return ; - case 'BlockMarkup': - return ; - case 'BlockForm': - return ; - case 'BlockImageTeasers': - return ( - // TODO: Implement BlockImageTeasers -
- BlockImageTeasers goes here -
- ); - case 'BlockCta': - return ; - case 'BlockImageWithText': - return ( - // TODO: Implement BlockImageWithText -
- BlockImageWithText goes here -
- ); - case 'BlockQuote': - return
; - default: - throw new UnreachableCaseError(block); - } - })} -
-
+ {page?.content?.filter(isTruthy).map((block, index) => { + switch (block.__typename) { + case 'BlockMedia': + return ; + case 'BlockMarkup': + return ; + case 'BlockForm': + return ; + case 'BlockImageTeasers': + return ; + case 'BlockCta': + return ; + case 'BlockImageWithText': + return ( + // TODO: Implement BlockImageWithText +
+ BlockImageWithText goes here +
+ ); + case 'BlockQuote': + return
; + default: + throw new UnreachableCaseError(block); + } + })}
); diff --git a/packages/ui/src/components/Routes/Page.stories.tsx b/packages/ui/src/components/Routes/Page.stories.tsx index f3d048c25..2b2478cd4 100644 --- a/packages/ui/src/components/Routes/Page.stories.tsx +++ b/packages/ui/src/components/Routes/Page.stories.tsx @@ -10,6 +10,7 @@ import { Meta, StoryObj } from '@storybook/react'; import React from 'react'; import { image } from '../../helpers/image'; +import { Default as BlockBackgroundImageCards } from '../Organisms/PageContent/BlockBackgroundImageCards.stories'; import { Mixed, Paragraph } from '../Organisms/PageContent/BlockMarkup.stories'; import { WithCaption } from '../Organisms/PageContent/BlockMedia.stories'; import { Default as FrameStory } from './Frame.stories'; @@ -47,6 +48,10 @@ export const Default = { ctaText: 'Call to action', }, content: [ + { + __typename: 'BlockImageTeasers', + ...BlockBackgroundImageCards.args, + }, { __typename: 'BlockMarkup', ...Mixed.args, @@ -59,6 +64,10 @@ export const Default = { __typename: 'BlockMarkup', ...Paragraph.args, }, + { + __typename: 'BlockImageTeasers', + ...BlockBackgroundImageCards.args, + }, ] as Exclude['content'], }, }, From 43a8349606cf8fd9b7db9e527328bdfcd549afea Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Fri, 3 May 2024 08:21:42 +0200 Subject: [PATCH 05/26] feat(SLB-287): update height on mobile --- .../Organisms/PageContent/BlockBackgroundImageCards.tsx | 4 ++-- packages/ui/src/tailwind.css | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx b/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx index 55783c56a..f3cbc828b 100644 --- a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx +++ b/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx @@ -25,10 +25,10 @@ export function BlockBackgroundImageCards(props: BlockImageTeasers) { export function BlockBackgroundImageCard(props: BlockImageTeaser) { return ( -
+
{props.image ? ( {props.image.alt} diff --git a/packages/ui/src/tailwind.css b/packages/ui/src/tailwind.css index 8e10ec3a8..a6ea88154 100644 --- a/packages/ui/src/tailwind.css +++ b/packages/ui/src/tailwind.css @@ -35,6 +35,9 @@ content: '' !important; } +/* Selects the .block-background-image-cards only when it is the first child of the
+ element immediately following the .default-hero section. + */ .default-hero + div > :first-child.block-background-image-cards { @apply mt-2 px-0; } \ No newline at end of file From d4fb4a1b67403fc086f7de379c552a8cf51e42b3 Mon Sep 17 00:00:00 2001 From: Alex Tkachev Date: Wed, 8 May 2024 17:56:25 +0400 Subject: [PATCH 06/26] chore: update default content to let tests pass --- .../content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml b/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml index dd286b895..ae90a656e 100644 --- a/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml +++ b/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml @@ -7,8 +7,8 @@ _meta: depends: 3a0fe860-a6d6-428a-9474-365bd57509aa: media 478c4289-961d-4ce8-85d6-578ae05f3019: media - 72187a1f-3e48-4b45-a9b7-189c6fd7ee26: media 5dfc1856-e9e4-4f02-9cd6-9d888870ce1a: media + 72187a1f-3e48-4b45-a9b7-189c6fd7ee26: media default: revision_uid: - @@ -104,7 +104,7 @@ default: - + @@ -115,7 +115,6 @@ default: - From 68a17301c7a6c8d92810c3c363fcb1848eb78413 Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Thu, 18 Apr 2024 11:51:11 +0300 Subject: [PATCH 07/26] feat(SLB-328): added the entity_create_split module --- .../entity_create_split.info.yml | 5 + .../entity_create_split.module | 30 +++ .../entity_create_split.routing.yml | 9 + .../entity_create_split.services.yml | 3 + .../EntityCreateSplitController.php | 35 ++++ .../src/FormOperations.php | 57 ++++++ .../src/FormOperationsInterface.php | 29 +++ .../tests/src/Unit/FormOperationsTest.php | 171 ++++++++++++++++++ 8 files changed, 339 insertions(+) create mode 100644 packages/drupal/entity_create_split/entity_create_split.info.yml create mode 100644 packages/drupal/entity_create_split/entity_create_split.module create mode 100644 packages/drupal/entity_create_split/entity_create_split.routing.yml create mode 100644 packages/drupal/entity_create_split/entity_create_split.services.yml create mode 100644 packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php create mode 100644 packages/drupal/entity_create_split/src/FormOperations.php create mode 100644 packages/drupal/entity_create_split/src/FormOperationsInterface.php create mode 100644 packages/drupal/entity_create_split/tests/src/Unit/FormOperationsTest.php diff --git a/packages/drupal/entity_create_split/entity_create_split.info.yml b/packages/drupal/entity_create_split/entity_create_split.info.yml new file mode 100644 index 000000000..b8d2ad45e --- /dev/null +++ b/packages/drupal/entity_create_split/entity_create_split.info.yml @@ -0,0 +1,5 @@ +name: Entity Create Split +type: module +description: 'Provides a route for splitting the entity creation in two steps: first for the mandatory fields and a second one, which is actually the edit form, for the rest of the fields.' +package: Custom +core_version_requirement: ^9 || ^10 diff --git a/packages/drupal/entity_create_split/entity_create_split.module b/packages/drupal/entity_create_split/entity_create_split.module new file mode 100644 index 000000000..b1bb96ec9 --- /dev/null +++ b/packages/drupal/entity_create_split/entity_create_split.module @@ -0,0 +1,30 @@ +getFormObject() instanceof EntityFormInterface) { + $entity = $form_state->getFormObject()->getEntity(); + if (!$entity->isNew() || empty($entity->hideOptionalFormFields)) { + return; + } + /* @var \Drupal\entity_create_split\FormOperationsInterface $formOperations */ + $formOperations = \Drupal::service('entity_create_split.form_operations'); + $formOperations->hideOptionalFields($form); + } +} diff --git a/packages/drupal/entity_create_split/entity_create_split.routing.yml b/packages/drupal/entity_create_split/entity_create_split.routing.yml new file mode 100644 index 000000000..e4890ef8a --- /dev/null +++ b/packages/drupal/entity_create_split/entity_create_split.routing.yml @@ -0,0 +1,9 @@ +entity_create_split.create: + path: '/entity/create/{entity_type}/{bundle}' + defaults: + _controller: '\Drupal\entity_create_split\Controller\EntityCreateSplitController::createForm' + _title: 'Create entity' + requirements: + _permission: 'administer graphql configuration' + options: + _admin_route: TRUE diff --git a/packages/drupal/entity_create_split/entity_create_split.services.yml b/packages/drupal/entity_create_split/entity_create_split.services.yml new file mode 100644 index 000000000..67480d616 --- /dev/null +++ b/packages/drupal/entity_create_split/entity_create_split.services.yml @@ -0,0 +1,3 @@ +services: + entity_create_split.form_operations: + class: Drupal\entity_create_split\FormOperations diff --git a/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php b/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php new file mode 100644 index 000000000..434af1835 --- /dev/null +++ b/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php @@ -0,0 +1,35 @@ +entityTypeManager()->hasDefinition($entity_type)) { + throw new NotFoundHttpException(); + } + $entityTypeDefinition = $this->entityTypeManager()->getDefinition($entity_type); + $bundleKey = $entityTypeDefinition->getKey('bundle'); + + // @todo: find a way to check if the bundle value is allowed. + $entity = $this->entityTypeManager()->getStorage($entity_type)->create([$bundleKey => $bundle]); + $entity->disableGutenberg = TRUE; + $entity->hideOptionalFormFields = TRUE; + $editForm = $this->entityTypeManager()->getFormObject($entity_type, 'default')->setEntity($entity); + return \Drupal::formBuilder()->getForm($editForm); + } +} diff --git a/packages/drupal/entity_create_split/src/FormOperations.php b/packages/drupal/entity_create_split/src/FormOperations.php new file mode 100644 index 000000000..454b0a2f8 --- /dev/null +++ b/packages/drupal/entity_create_split/src/FormOperations.php @@ -0,0 +1,57 @@ +hideOptionalFields($elements[$child]); + if ($this->shouldHideField($elements[$child])) { + $elements[$child]['#access'] = FALSE; + } + // Set the $allChildrenHidden flag to false in case this child is allowed + // to be rendered. + if (!isset($elements[$child]['#access']) || $elements[$child]['#access']) { + $allChildrenHidden = FALSE; + } + } + // And let's check now if we should hide the element completely (if all its + // children are hidden). + if ($allChildrenHidden) { + $elements['#access'] = FALSE; + } + } + + /** + * {@inheritDoc} + */ + public function shouldHideField(array $field): bool { + // Right now, we deny the access of all the elements which are not hidden or + // value, and they have the #required flag set to false. + return isset($field['#type']) && + $field['#type'] !== 'hidden' && + $field['#type'] !== 'value' && + isset($field['#required']) && + $field['#required'] === FALSE; + } +} diff --git a/packages/drupal/entity_create_split/src/FormOperationsInterface.php b/packages/drupal/entity_create_split/src/FormOperationsInterface.php new file mode 100644 index 000000000..bd881a65b --- /dev/null +++ b/packages/drupal/entity_create_split/src/FormOperationsInterface.php @@ -0,0 +1,29 @@ +formOperations = new FormOperations(); + } + + /** + * @covers \Drupal\entity_create_split\FormOperations::shouldHideField + * @dataProvider shouldHideFieldProvider + */ + public function testShouldHideField(array $field, bool $expected) { + $this->assertEquals($expected, $this->formOperations->shouldHideField($field)); + } + + public function shouldHideFieldProvider() { + return [ + // 1. No type set on the form element. + [ + [ + '#property1' => 'lorem', + '#property2' => 'ipsum', + 'children' => ['dolor'], + ], + FALSE, + ], + // 2. A hidden field. + [ + [ + '#type' => 'hidden', + '#value' => 'this is a hidden field', + ], + FALSE, + ], + // 3. A value field. + [ + [ + '#type' => 'value', + '#value' => 'this is a value field', + ], + FALSE, + ], + // 3. A required field. + [ + [ + '#type' => 'textfield', + '#title' => 'this is a text field', + '#required' => TRUE, + ], + FALSE, + ], + // 3. An optional field. + [ + [ + '#type' => 'textfield', + '#title' => 'this is a text field', + '#required' => FALSE, + ], + TRUE, + ], + // 3. A field with no required property set (this should not be hidden. + // To hide a field, the required property has to be specified). + [ + [ + '#type' => 'textfield', + '#title' => 'this is a text field', + ], + FALSE, + ], + ]; + } + + /** + * @covers \Drupal\entity_create_split\FormOperations::hideOptionalFields + */ + public function testHideOptionalFields() { + $formStructure = [ + '#some_attribute' => 'some_value', + 'optional' => [ + '#type' => 'textfield', + '#title' => 'optional textfield', + '#required' => FALSE, + ], + 'required' => [ + '#type' => 'textfield', + '#title' => 'required textfield', + '#required' => TRUE, + ], + 'optional_group' => [ + '#title' => 'optional group', + 'first_element' => [ + '#title' => 'First optional element', + 'widget' => [ + '#type' => 'select', + '#required' => FALSE, + ], + ], + 'second_element' => [ + '#title' => 'Second optional element', + 'widget' => [ + '#type' => 'checkboxes', + '#required' => FALSE, + ], + ], + ], + 'required_group' => [ + '#title' => 'required group', + 'first_element' => [ + '#title' => 'First required element', + 'widget' => [ + '#type' => 'textfield', + '#required' => TRUE, + ], + ], + 'second_element' => [ + '#title' => 'Second required element', + 'widget' => [ + '#type' => 'textarea', + '#required' => TRUE, + ], + ], + ], + 'mixed_group' => [ + '#title' => 'mixed group', + 'first_element' => [ + '#title' => 'First mixed element - required', + 'widget' => [ + '#type' => 'radios', + '#required' => TRUE, + ], + ], + 'second_element' => [ + '#title' => 'Second mixed element - optional', + 'widget' => [ + '#type' => 'textarea', + '#required' => FALSE, + ], + ], + ], + ]; + $expectedResult = $formStructure; + $expectedResult['optional']['#access'] = FALSE; + $expectedResult['optional_group']['first_element']['widget']['#access'] = FALSE; + $expectedResult['optional_group']['second_element']['widget']['#access'] = FALSE; + $expectedResult['optional_group']['first_element']['#access'] = FALSE; + $expectedResult['optional_group']['second_element']['#access'] = FALSE; + $expectedResult['optional_group']['#access'] = FALSE; + $expectedResult['mixed_group']['second_element']['widget']['#access'] = FALSE; + $expectedResult['mixed_group']['second_element']['#access'] = FALSE; + $this->formOperations->hideOptionalFields($formStructure); + $this->assertEquals($expectedResult, $formStructure); + } + +} From f8b67c8a10ac2a43b71ea1d201bb739004f1b069 Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Thu, 18 Apr 2024 16:16:57 +0300 Subject: [PATCH 08/26] feat(SLB-328): add readme and access checks on the entity creation route --- packages/drupal/entity_create_split/README.md | 13 +++++++ .../entity_create_split.module | 9 ++--- .../entity_create_split.routing.yml | 4 +- .../EntityCreateSplitController.php | 39 ++++++++++++++++--- 4 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 packages/drupal/entity_create_split/README.md diff --git a/packages/drupal/entity_create_split/README.md b/packages/drupal/entity_create_split/README.md new file mode 100644 index 000000000..4a9c81851 --- /dev/null +++ b/packages/drupal/entity_create_split/README.md @@ -0,0 +1,13 @@ +# Entity Create Split + +A Drupal module which exposes routes to split an entity create form into two parts: +- on the first step, only the required fields are presented. After submitting the form, a entity is already created +- the second step is actually just the entity edit form, containing all the other optional form fields. + +One important thing is that there are no alterations done on the current entity create routes (for example, on /node/add/page). This module just exposes the route _/entity/create/{entity_type}/{entity_bundle}_ and how this route is used is subject to each specific project. + +## Special case for the Gutenberg editor + +The Gutenberg editor does a lot of alterations on the create form. For this reason, it is better that the form alter hook of the gutenberg module to not run at all. This is not easy possible, so right now the easieist approach is to just patch the module. The patch is included in the _patches/gutenberg_ folder. + +The functionality should also work without the patch, but the initial form will not look that nice. diff --git a/packages/drupal/entity_create_split/entity_create_split.module b/packages/drupal/entity_create_split/entity_create_split.module index b1bb96ec9..0058cf93f 100644 --- a/packages/drupal/entity_create_split/entity_create_split.module +++ b/packages/drupal/entity_create_split/entity_create_split.module @@ -9,18 +9,17 @@ use Drupal\Core\Entity\EntityFormInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element; /** * Implements hook_form_alter(). */ function entity_create_split_form_alter(&$form, FormStateInterface $form_state, $form_id) { - // We are only interested in entity create forms, and only when the - // splitRequiredFields flag is set on the entity (which means the form was - // requested from the entity_create_split.create route. + // We are only interested in entity forms, and only when the + // hideOptionalFormFields flag is set on the entity (which means the form was + // requested from the entity_create_split.create route). if ($form_state->getFormObject() instanceof EntityFormInterface) { $entity = $form_state->getFormObject()->getEntity(); - if (!$entity->isNew() || empty($entity->hideOptionalFormFields)) { + if (!$entity->hideOptionalFormFields) { return; } /* @var \Drupal\entity_create_split\FormOperationsInterface $formOperations */ diff --git a/packages/drupal/entity_create_split/entity_create_split.routing.yml b/packages/drupal/entity_create_split/entity_create_split.routing.yml index e4890ef8a..e5762db49 100644 --- a/packages/drupal/entity_create_split/entity_create_split.routing.yml +++ b/packages/drupal/entity_create_split/entity_create_split.routing.yml @@ -2,8 +2,8 @@ entity_create_split.create: path: '/entity/create/{entity_type}/{bundle}' defaults: _controller: '\Drupal\entity_create_split\Controller\EntityCreateSplitController::createForm' - _title: 'Create entity' + _title_callback: '\Drupal\entity_create_split\Controller\EntityCreateSplitController::getTitle' requirements: - _permission: 'administer graphql configuration' + _custom_access: '\Drupal\entity_create_split\Controller\EntityCreateSplitController::access' options: _admin_route: TRUE diff --git a/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php b/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php index 434af1835..fa1b39bd5 100644 --- a/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php +++ b/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php @@ -2,8 +2,8 @@ namespace Drupal\entity_create_split\Controller; +use Drupal\Core\Access\AccessResult; use Drupal\Core\Controller\ControllerBase; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class EntityCreateSplitController extends ControllerBase { @@ -19,17 +19,44 @@ class EntityCreateSplitController extends ControllerBase { * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ public function createForm($entity_type, $bundle) { - if (!$this->entityTypeManager()->hasDefinition($entity_type)) { - throw new NotFoundHttpException(); - } $entityTypeDefinition = $this->entityTypeManager()->getDefinition($entity_type); $bundleKey = $entityTypeDefinition->getKey('bundle'); - - // @todo: find a way to check if the bundle value is allowed. $entity = $this->entityTypeManager()->getStorage($entity_type)->create([$bundleKey => $bundle]); $entity->disableGutenberg = TRUE; $entity->hideOptionalFormFields = TRUE; $editForm = $this->entityTypeManager()->getFormObject($entity_type, 'default')->setEntity($entity); return \Drupal::formBuilder()->getForm($editForm); } + + /** + * Title callback for the createForm() route. + */ + public function getTitle($entity_type, $bundle) { + $entityTypeDefinition = $this->entityTypeManager()->getDefinition($entity_type); + $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); + $bundleLabel = $this->entityTypeManager()->getStorage($bundleEntityType)->load($bundle)->label(); + return $this->t("Create %entity_type: %entity_bundle", [ + '%entity_type' => $entityTypeDefinition->getLabel(), + '%entity_bundle' => $bundleLabel, + ]); + } + + /** + * Access callback for the createForm() route. + */ + public function access($entity_type, $bundle) { + if (!$this->entityTypeManager()->hasDefinition($entity_type)) { + return AccessResult::forbidden(); + } + $entityTypeDefinition = $this->entityTypeManager()->getDefinition($entity_type); + $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); + $bundleEntity = $this->entityTypeManager()->getStorage($bundleEntityType)->load($bundle); + if (!$bundleEntity) { + return AccessResult::forbidden(); + } + + return $this->entityTypeManager() + ->getAccessControlHandler($entity_type) + ->createAccess($bundle, NULL, [], TRUE); + } } From 984979e3c87aa663f036447b36935e303b1547bb Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Thu, 18 Apr 2024 16:26:07 +0300 Subject: [PATCH 09/26] feat(SLB-328): added the disable gutenberg flag patch --- .../gutenberg/disable_gutenberg_flag.patch | 15 +++++++++++++++ .../gutenberg/disable_gutenberg_flag.patch | 15 +++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 apps/cms/patches/contrib/gutenberg/disable_gutenberg_flag.patch create mode 100644 packages/drupal/entity_create_split/patches/gutenberg/disable_gutenberg_flag.patch diff --git a/apps/cms/patches/contrib/gutenberg/disable_gutenberg_flag.patch b/apps/cms/patches/contrib/gutenberg/disable_gutenberg_flag.patch new file mode 100644 index 000000000..56d6464d6 --- /dev/null +++ b/apps/cms/patches/contrib/gutenberg/disable_gutenberg_flag.patch @@ -0,0 +1,15 @@ +diff --git a/gutenberg.module b/gutenberg.module +index dec6b80..173c424 100644 +--- a/gutenberg.module ++++ b/gutenberg.module +@@ -1042,6 +1042,10 @@ function _gutenberg_is_gutenberg_enabled(EntityInterface $entity = NULL) { + return FALSE; + } + ++ if (isset($entity->disableGutenberg) && $entity->disableGutenberg === TRUE) { ++ return FALSE; ++ } ++ + if ($entity->getEntityTypeId() !== 'node') { + return FALSE; + } diff --git a/packages/drupal/entity_create_split/patches/gutenberg/disable_gutenberg_flag.patch b/packages/drupal/entity_create_split/patches/gutenberg/disable_gutenberg_flag.patch new file mode 100644 index 000000000..56d6464d6 --- /dev/null +++ b/packages/drupal/entity_create_split/patches/gutenberg/disable_gutenberg_flag.patch @@ -0,0 +1,15 @@ +diff --git a/gutenberg.module b/gutenberg.module +index dec6b80..173c424 100644 +--- a/gutenberg.module ++++ b/gutenberg.module +@@ -1042,6 +1042,10 @@ function _gutenberg_is_gutenberg_enabled(EntityInterface $entity = NULL) { + return FALSE; + } + ++ if (isset($entity->disableGutenberg) && $entity->disableGutenberg === TRUE) { ++ return FALSE; ++ } ++ + if ($entity->getEntityTypeId() !== 'node') { + return FALSE; + } From 18edf214aeddaafd638188da9394b33df075c5dc Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Thu, 18 Apr 2024 16:41:45 +0300 Subject: [PATCH 10/26] feat(SLB-328): apply the gutenberg patch --- apps/cms/composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/cms/composer.json b/apps/cms/composer.json index bb6feeb43..11008faf5 100644 --- a/apps/cms/composer.json +++ b/apps/cms/composer.json @@ -99,7 +99,10 @@ "Fix site install": "https://www.drupal.org/files/issues/2023-07-28/3349663-8.patch" }, "amazeelabs/silverback_gatsby": { - "Autosave preview": "./patches/fetch-entity.patch" + "Autosave preview": "./patches/fetch-entity.patch" + }, + "drupal/gutenberg": { + "Disable gutenberg flag": "patches/contrib/gutenberg/disable_gutenberg_flag.patch" } }, "patchLevel": { From edaebc848c7dfa7ceb8e41e1180593a17d8bc46d Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Tue, 7 May 2024 11:59:27 +0300 Subject: [PATCH 11/26] feat(SLB-328): split entity creation based on a form display mode --- ...re.entity_form_display.node.page.split.yml | 103 +++++++++++ .../sync/core.entity_form_mode.node.split.yml | 11 ++ apps/cms/config/sync/core.extension.yml | 1 + packages/drupal/entity_create_split/README.md | 4 +- .../entity_create_split.module | 20 +- .../entity_create_split.services.yml | 10 +- .../EntityCreateSplitController.php | 3 +- .../EntityCreateSplitRequestSubscriber.php | 108 +++++++++++ .../src/FormOperations.php | 57 ------ .../src/FormOperationsInterface.php | 29 --- .../tests/src/Unit/FormOperationsTest.php | 171 ------------------ 11 files changed, 240 insertions(+), 277 deletions(-) create mode 100644 apps/cms/config/sync/core.entity_form_display.node.page.split.yml create mode 100644 apps/cms/config/sync/core.entity_form_mode.node.split.yml create mode 100644 packages/drupal/entity_create_split/src/EventSubscriber/EntityCreateSplitRequestSubscriber.php delete mode 100644 packages/drupal/entity_create_split/src/FormOperations.php delete mode 100644 packages/drupal/entity_create_split/src/FormOperationsInterface.php delete mode 100644 packages/drupal/entity_create_split/tests/src/Unit/FormOperationsTest.php diff --git a/apps/cms/config/sync/core.entity_form_display.node.page.split.yml b/apps/cms/config/sync/core.entity_form_display.node.page.split.yml new file mode 100644 index 000000000..7c4e46ca1 --- /dev/null +++ b/apps/cms/config/sync/core.entity_form_display.node.page.split.yml @@ -0,0 +1,103 @@ +uuid: a39d0011-2ebf-4b18-a9f0-8b5aeff5b9aa +langcode: en +status: true +dependencies: + config: + - core.entity_form_mode.node.split + - field.field.node.page.body + - field.field.node.page.field_metatags + - node.type.page + - workflows.workflow.basic + module: + - content_moderation + - metatag + - path +id: node.page.split +targetEntityType: node +bundle: page +mode: split +content: + created: + type: datetime_timestamp + weight: 3 + region: content + settings: { } + third_party_settings: { } + field_metatags: + type: metatag_firehose + weight: 11 + region: content + settings: + sidebar: true + use_details: true + third_party_settings: { } + langcode: + type: language_select + weight: 1 + region: content + settings: + include_locked: true + third_party_settings: { } + moderation_state: + type: moderation_state_default + weight: 9 + region: content + settings: { } + third_party_settings: { } + path: + type: path + weight: 7 + region: content + settings: { } + third_party_settings: { } + promote: + type: boolean_checkbox + weight: 5 + region: content + settings: + display_label: true + third_party_settings: { } + status: + type: boolean_checkbox + weight: 10 + region: content + settings: + display_label: true + third_party_settings: { } + sticky: + type: boolean_checkbox + weight: 6 + region: content + settings: + display_label: true + third_party_settings: { } + title: + type: string_textfield + weight: 0 + region: content + settings: + size: 60 + placeholder: '' + third_party_settings: { } + translation: + weight: 4 + region: content + settings: { } + third_party_settings: { } + uid: + type: entity_reference_autocomplete + weight: 2 + region: content + settings: + match_operator: CONTAINS + match_limit: 10 + size: 60 + placeholder: '' + third_party_settings: { } + url_redirects: + weight: 8 + region: content + settings: { } + third_party_settings: { } +hidden: + body: true diff --git a/apps/cms/config/sync/core.entity_form_mode.node.split.yml b/apps/cms/config/sync/core.entity_form_mode.node.split.yml new file mode 100644 index 000000000..8e665c318 --- /dev/null +++ b/apps/cms/config/sync/core.entity_form_mode.node.split.yml @@ -0,0 +1,11 @@ +uuid: 5f26940d-fd06-41e0-8e59-9dfcdc9c86df +langcode: en +status: true +dependencies: + module: + - node +id: node.split +label: Split +description: '' +targetEntityType: node +cache: true diff --git a/apps/cms/config/sync/core.extension.yml b/apps/cms/config/sync/core.extension.yml index c32cf22ef..84b5d6503 100644 --- a/apps/cms/config/sync/core.extension.yml +++ b/apps/cms/config/sync/core.extension.yml @@ -22,6 +22,7 @@ module: dropzonejs: 0 dynamic_page_cache: 0 editor: 0 + entity_create_split: 0 entity_usage: 0 environment_indicator: 0 field: 0 diff --git a/packages/drupal/entity_create_split/README.md b/packages/drupal/entity_create_split/README.md index 4a9c81851..51d58ef2c 100644 --- a/packages/drupal/entity_create_split/README.md +++ b/packages/drupal/entity_create_split/README.md @@ -4,10 +4,10 @@ A Drupal module which exposes routes to split an entity create form into two par - on the first step, only the required fields are presented. After submitting the form, a entity is already created - the second step is actually just the entity edit form, containing all the other optional form fields. -One important thing is that there are no alterations done on the current entity create routes (for example, on /node/add/page). This module just exposes the route _/entity/create/{entity_type}/{entity_bundle}_ and how this route is used is subject to each specific project. +To enable this feature, you must create a form mode with the machine name "split" and enable it on the bundle for which you want to have this feature. ## Special case for the Gutenberg editor -The Gutenberg editor does a lot of alterations on the create form. For this reason, it is better that the form alter hook of the gutenberg module to not run at all. This is not easy possible, so right now the easieist approach is to just patch the module. The patch is included in the _patches/gutenberg_ folder. +The Gutenberg editor does a lot of alterations on the create form. For this reason, it is better that the form alter hook of the gutenberg module to not run at all. This is not easy possible, so right now the easiest approach is to just patch the module. The patch is included in the _patches/gutenberg_ folder. The functionality should also work without the patch, but the initial form will not look that nice. diff --git a/packages/drupal/entity_create_split/entity_create_split.module b/packages/drupal/entity_create_split/entity_create_split.module index 0058cf93f..8b87b1145 100644 --- a/packages/drupal/entity_create_split/entity_create_split.module +++ b/packages/drupal/entity_create_split/entity_create_split.module @@ -7,23 +7,15 @@ * the rest of the fields. */ -use Drupal\Core\Entity\EntityFormInterface; -use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Entity\ContentEntityType; /** - * Implements hook_form_alter(). + * Implements hook_entity_type_build(). */ -function entity_create_split_form_alter(&$form, FormStateInterface $form_state, $form_id) { - // We are only interested in entity forms, and only when the - // hideOptionalFormFields flag is set on the entity (which means the form was - // requested from the entity_create_split.create route). - if ($form_state->getFormObject() instanceof EntityFormInterface) { - $entity = $form_state->getFormObject()->getEntity(); - if (!$entity->hideOptionalFormFields) { - return; +function entity_create_split_entity_type_build(array &$entity_types) { + foreach ($entity_types as $entity_type) { + if ($entity_type instanceof ContentEntityType) { + $entity_type->setFormClass('split', $entity_type->getFormClass('default')); } - /* @var \Drupal\entity_create_split\FormOperationsInterface $formOperations */ - $formOperations = \Drupal::service('entity_create_split.form_operations'); - $formOperations->hideOptionalFields($form); } } diff --git a/packages/drupal/entity_create_split/entity_create_split.services.yml b/packages/drupal/entity_create_split/entity_create_split.services.yml index 67480d616..4216b1192 100644 --- a/packages/drupal/entity_create_split/entity_create_split.services.yml +++ b/packages/drupal/entity_create_split/entity_create_split.services.yml @@ -1,3 +1,9 @@ services: - entity_create_split.form_operations: - class: Drupal\entity_create_split\FormOperations + entity_create_split.route_subscriber: + class: Drupal\entity_create_split\EventSubscriber\EntityCreateSplitRequestSubscriber + arguments: + - "@current_route_match" + - "@entity_type.manager" + - "@entity_display.repository" + tags: + - { name: event_subscriber } diff --git a/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php b/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php index fa1b39bd5..f27b6b2fa 100644 --- a/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php +++ b/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php @@ -23,8 +23,7 @@ public function createForm($entity_type, $bundle) { $bundleKey = $entityTypeDefinition->getKey('bundle'); $entity = $this->entityTypeManager()->getStorage($entity_type)->create([$bundleKey => $bundle]); $entity->disableGutenberg = TRUE; - $entity->hideOptionalFormFields = TRUE; - $editForm = $this->entityTypeManager()->getFormObject($entity_type, 'default')->setEntity($entity); + $editForm = $this->entityTypeManager()->getFormObject($entity_type, 'split')->setEntity($entity); return \Drupal::formBuilder()->getForm($editForm); } diff --git a/packages/drupal/entity_create_split/src/EventSubscriber/EntityCreateSplitRequestSubscriber.php b/packages/drupal/entity_create_split/src/EventSubscriber/EntityCreateSplitRequestSubscriber.php new file mode 100644 index 000000000..10f325d37 --- /dev/null +++ b/packages/drupal/entity_create_split/src/EventSubscriber/EntityCreateSplitRequestSubscriber.php @@ -0,0 +1,108 @@ +routeMatch = $route_match; + $this->entityTypeManager = $entity_type_manager; + $this->entityDisplayRepository = $entity_display_repository; + } + + /** + * {@inheritDoc} + */ + public static function getSubscribedEvents() { + $events[KernelEvents::REQUEST][] = ['onKernelRequest', 0]; + return $events; + } + + public function onKernelRequest(RequestEvent $event) { + $supportedRoutes = $this->supportedRoutes(); + $currentRouteName = $this->routeMatch->getRouteName(); + if (empty($supportedRoutes[$currentRouteName])) { + return; + } + $entityTypeId = $supportedRoutes[$currentRouteName]; + $entityTypeDefinition =$this->entityTypeManager->getDefinition($entityTypeId); + // Make sure the entity has the split form class handler defined. + if (!$entityTypeDefinition->getFormClass('split')) { + return; + } + $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); + $bundle = $this->routeMatch->getParameter($bundleEntityType); + // If, for some reason, we can't load the bundle from the current request, + // then we just stop here. + if (empty($bundle)) { + return; + } + $formModes = $this->entityDisplayRepository->getFormModeOptionsByBundle($entityTypeId, $bundle->id()); + // If the "split" form mode is not setup on the current entity bundle, then + // we also just stop here. + if (empty($formModes['split'])) { + return; + } + + // If we got here, it means that we are on a entity add route, and the + // entity bundle has the split form mode configure, which means we need to + // redirect to the entity_create_split.create route for that entity bundle. + $createSplitUrl = Url::fromRoute('entity_create_split.create', [ + 'entity_type' => $entityTypeId, + 'bundle' => $bundle->id(), + ]); + $response = new TrustedRedirectResponse($createSplitUrl->setAbsolute() + ->toString(), 302); + $event->setResponse($response); + } + + /** + * Helper, temporary, method to define the routes which are checked by the + * event subscriber for redirecting the user to the split form. + * + * @return string[] + */ + protected function supportedRoutes() { + return [ + 'node.add' => 'node', + 'media.add' => 'media', + ]; + } +} diff --git a/packages/drupal/entity_create_split/src/FormOperations.php b/packages/drupal/entity_create_split/src/FormOperations.php deleted file mode 100644 index 454b0a2f8..000000000 --- a/packages/drupal/entity_create_split/src/FormOperations.php +++ /dev/null @@ -1,57 +0,0 @@ -hideOptionalFields($elements[$child]); - if ($this->shouldHideField($elements[$child])) { - $elements[$child]['#access'] = FALSE; - } - // Set the $allChildrenHidden flag to false in case this child is allowed - // to be rendered. - if (!isset($elements[$child]['#access']) || $elements[$child]['#access']) { - $allChildrenHidden = FALSE; - } - } - // And let's check now if we should hide the element completely (if all its - // children are hidden). - if ($allChildrenHidden) { - $elements['#access'] = FALSE; - } - } - - /** - * {@inheritDoc} - */ - public function shouldHideField(array $field): bool { - // Right now, we deny the access of all the elements which are not hidden or - // value, and they have the #required flag set to false. - return isset($field['#type']) && - $field['#type'] !== 'hidden' && - $field['#type'] !== 'value' && - isset($field['#required']) && - $field['#required'] === FALSE; - } -} diff --git a/packages/drupal/entity_create_split/src/FormOperationsInterface.php b/packages/drupal/entity_create_split/src/FormOperationsInterface.php deleted file mode 100644 index bd881a65b..000000000 --- a/packages/drupal/entity_create_split/src/FormOperationsInterface.php +++ /dev/null @@ -1,29 +0,0 @@ -formOperations = new FormOperations(); - } - - /** - * @covers \Drupal\entity_create_split\FormOperations::shouldHideField - * @dataProvider shouldHideFieldProvider - */ - public function testShouldHideField(array $field, bool $expected) { - $this->assertEquals($expected, $this->formOperations->shouldHideField($field)); - } - - public function shouldHideFieldProvider() { - return [ - // 1. No type set on the form element. - [ - [ - '#property1' => 'lorem', - '#property2' => 'ipsum', - 'children' => ['dolor'], - ], - FALSE, - ], - // 2. A hidden field. - [ - [ - '#type' => 'hidden', - '#value' => 'this is a hidden field', - ], - FALSE, - ], - // 3. A value field. - [ - [ - '#type' => 'value', - '#value' => 'this is a value field', - ], - FALSE, - ], - // 3. A required field. - [ - [ - '#type' => 'textfield', - '#title' => 'this is a text field', - '#required' => TRUE, - ], - FALSE, - ], - // 3. An optional field. - [ - [ - '#type' => 'textfield', - '#title' => 'this is a text field', - '#required' => FALSE, - ], - TRUE, - ], - // 3. A field with no required property set (this should not be hidden. - // To hide a field, the required property has to be specified). - [ - [ - '#type' => 'textfield', - '#title' => 'this is a text field', - ], - FALSE, - ], - ]; - } - - /** - * @covers \Drupal\entity_create_split\FormOperations::hideOptionalFields - */ - public function testHideOptionalFields() { - $formStructure = [ - '#some_attribute' => 'some_value', - 'optional' => [ - '#type' => 'textfield', - '#title' => 'optional textfield', - '#required' => FALSE, - ], - 'required' => [ - '#type' => 'textfield', - '#title' => 'required textfield', - '#required' => TRUE, - ], - 'optional_group' => [ - '#title' => 'optional group', - 'first_element' => [ - '#title' => 'First optional element', - 'widget' => [ - '#type' => 'select', - '#required' => FALSE, - ], - ], - 'second_element' => [ - '#title' => 'Second optional element', - 'widget' => [ - '#type' => 'checkboxes', - '#required' => FALSE, - ], - ], - ], - 'required_group' => [ - '#title' => 'required group', - 'first_element' => [ - '#title' => 'First required element', - 'widget' => [ - '#type' => 'textfield', - '#required' => TRUE, - ], - ], - 'second_element' => [ - '#title' => 'Second required element', - 'widget' => [ - '#type' => 'textarea', - '#required' => TRUE, - ], - ], - ], - 'mixed_group' => [ - '#title' => 'mixed group', - 'first_element' => [ - '#title' => 'First mixed element - required', - 'widget' => [ - '#type' => 'radios', - '#required' => TRUE, - ], - ], - 'second_element' => [ - '#title' => 'Second mixed element - optional', - 'widget' => [ - '#type' => 'textarea', - '#required' => FALSE, - ], - ], - ], - ]; - $expectedResult = $formStructure; - $expectedResult['optional']['#access'] = FALSE; - $expectedResult['optional_group']['first_element']['widget']['#access'] = FALSE; - $expectedResult['optional_group']['second_element']['widget']['#access'] = FALSE; - $expectedResult['optional_group']['first_element']['#access'] = FALSE; - $expectedResult['optional_group']['second_element']['#access'] = FALSE; - $expectedResult['optional_group']['#access'] = FALSE; - $expectedResult['mixed_group']['second_element']['widget']['#access'] = FALSE; - $expectedResult['mixed_group']['second_element']['#access'] = FALSE; - $this->formOperations->hideOptionalFields($formStructure); - $this->assertEquals($expectedResult, $formStructure); - } - -} From 1ed81673355bde2f566d1c42ae83034c66d5c324 Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Tue, 7 May 2024 14:32:19 +0300 Subject: [PATCH 12/26] chore(SLB-328): move the gutenberg module patch to contrib --- apps/cms/composer.json | 2 +- apps/cms/patches/.gitkeep | 0 .../gutenberg/disable_gutenberg_flag.patch | 15 --------------- packages/drupal/entity_create_split/README.md | 2 +- .../entity_create_split.module | 10 ++++++++++ 5 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 apps/cms/patches/.gitkeep delete mode 100644 apps/cms/patches/contrib/gutenberg/disable_gutenberg_flag.patch diff --git a/apps/cms/composer.json b/apps/cms/composer.json index 11008faf5..cd46fb033 100644 --- a/apps/cms/composer.json +++ b/apps/cms/composer.json @@ -102,7 +102,7 @@ "Autosave preview": "./patches/fetch-entity.patch" }, "drupal/gutenberg": { - "Disable gutenberg flag": "patches/contrib/gutenberg/disable_gutenberg_flag.patch" + "Gutenberg enabled hook": "https://www.drupal.org/files/issues/2024-05-07/gutenberg_enabled_hook_3445677-2.patch" } }, "patchLevel": { diff --git a/apps/cms/patches/.gitkeep b/apps/cms/patches/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/cms/patches/contrib/gutenberg/disable_gutenberg_flag.patch b/apps/cms/patches/contrib/gutenberg/disable_gutenberg_flag.patch deleted file mode 100644 index 56d6464d6..000000000 --- a/apps/cms/patches/contrib/gutenberg/disable_gutenberg_flag.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/gutenberg.module b/gutenberg.module -index dec6b80..173c424 100644 ---- a/gutenberg.module -+++ b/gutenberg.module -@@ -1042,6 +1042,10 @@ function _gutenberg_is_gutenberg_enabled(EntityInterface $entity = NULL) { - return FALSE; - } - -+ if (isset($entity->disableGutenberg) && $entity->disableGutenberg === TRUE) { -+ return FALSE; -+ } -+ - if ($entity->getEntityTypeId() !== 'node') { - return FALSE; - } diff --git a/packages/drupal/entity_create_split/README.md b/packages/drupal/entity_create_split/README.md index 51d58ef2c..12a79a2d2 100644 --- a/packages/drupal/entity_create_split/README.md +++ b/packages/drupal/entity_create_split/README.md @@ -8,6 +8,6 @@ To enable this feature, you must create a form mode with the machine name "split ## Special case for the Gutenberg editor -The Gutenberg editor does a lot of alterations on the create form. For this reason, it is better that the form alter hook of the gutenberg module to not run at all. This is not easy possible, so right now the easiest approach is to just patch the module. The patch is included in the _patches/gutenberg_ folder. +The Gutenberg editor does a lot of alterations on the create form. For this reason, it is better that the form alter hook of the gutenberg module to not run at all. This is not easy possible, so right now the easiest approach is to just patch the module with the patch from https://www.drupal.org/project/gutenberg/issues/3445677/ The functionality should also work without the patch, but the initial form will not look that nice. diff --git a/packages/drupal/entity_create_split/entity_create_split.module b/packages/drupal/entity_create_split/entity_create_split.module index 8b87b1145..2ae2e596f 100644 --- a/packages/drupal/entity_create_split/entity_create_split.module +++ b/packages/drupal/entity_create_split/entity_create_split.module @@ -8,6 +8,7 @@ */ use Drupal\Core\Entity\ContentEntityType; +use Drupal\Core\Entity\EntityInterface; /** * Implements hook_entity_type_build(). @@ -19,3 +20,12 @@ function entity_create_split_entity_type_build(array &$entity_types) { } } } + +/** + * Implements hook_gutenberg_enabled(). + */ +function entity_create_split_gutenberg_enabled(EntityInterface $entity) { + if (isset($entity->disableGutenberg) && $entity->disableGutenberg === TRUE) { + return FALSE; + } +} From 04416a43346707529f66de5a84659a62ea3faefc Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Tue, 7 May 2024 14:37:26 +0300 Subject: [PATCH 13/26] chore(SLB-328): remove the leftover patch from the module --- .../gutenberg/disable_gutenberg_flag.patch | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 packages/drupal/entity_create_split/patches/gutenberg/disable_gutenberg_flag.patch diff --git a/packages/drupal/entity_create_split/patches/gutenberg/disable_gutenberg_flag.patch b/packages/drupal/entity_create_split/patches/gutenberg/disable_gutenberg_flag.patch deleted file mode 100644 index 56d6464d6..000000000 --- a/packages/drupal/entity_create_split/patches/gutenberg/disable_gutenberg_flag.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/gutenberg.module b/gutenberg.module -index dec6b80..173c424 100644 ---- a/gutenberg.module -+++ b/gutenberg.module -@@ -1042,6 +1042,10 @@ function _gutenberg_is_gutenberg_enabled(EntityInterface $entity = NULL) { - return FALSE; - } - -+ if (isset($entity->disableGutenberg) && $entity->disableGutenberg === TRUE) { -+ return FALSE; -+ } -+ - if ($entity->getEntityTypeId() !== 'node') { - return FALSE; - } From 79877f93a74f1e69aecbd0ab05dc6a44e4186c2c Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Tue, 7 May 2024 14:43:31 +0300 Subject: [PATCH 14/26] fix(SLB-328): prettier fix --- apps/cms/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/cms/composer.json b/apps/cms/composer.json index cd46fb033..50427ad02 100644 --- a/apps/cms/composer.json +++ b/apps/cms/composer.json @@ -99,7 +99,7 @@ "Fix site install": "https://www.drupal.org/files/issues/2023-07-28/3349663-8.patch" }, "amazeelabs/silverback_gatsby": { - "Autosave preview": "./patches/fetch-entity.patch" + "Autosave preview": "./patches/fetch-entity.patch" }, "drupal/gutenberg": { "Gutenberg enabled hook": "https://www.drupal.org/files/issues/2024-05-07/gutenberg_enabled_hook_3445677-2.patch" From 7845458f6d0bd5a0e28998b3b0ef3e423b28dd2a Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Tue, 7 May 2024 15:43:57 +0300 Subject: [PATCH 15/26] test(SLB-328): update e2e tests --- tests/e2e/specs/drupal/content-editing.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/e2e/specs/drupal/content-editing.spec.ts b/tests/e2e/specs/drupal/content-editing.spec.ts index 463d49dc7..b90810480 100644 --- a/tests/e2e/specs/drupal/content-editing.spec.ts +++ b/tests/e2e/specs/drupal/content-editing.spec.ts @@ -6,19 +6,21 @@ test.describe('content-editing', () => { test.use({ storageState: '.auth/admin.json' }); test('moderation controls are placed in the sidebar', async ({ page }) => { - await page.goto(cmsUrl('/node/add/page')); + await page.goto(cmsUrl('/drupal')); + await page.locator('li.tabs__tab a:text("Edit")').click(); await expect( - page.locator('[aria-label="Editor settings"]').getByText('Save as'), + page.locator('[aria-label="Editor settings"]').getByText('Change to'), ).toBeVisible(); }); test('"More settings" fieldset is removed', async ({ page }) => { + await page.goto(cmsUrl('/drupal')); + await page.locator('li.tabs__tab a:text("Edit")').click(); // Why we expect it to be removed: // - It's too long to scroll to the bottom of long pages // - If we have any valuable controls in the "More settings" fieldset, // we should move them to the sidebar, where they are much easier to // access - await page.goto(cmsUrl('/node/add/page')); await expect(page.locator(':text-is("More settings")')).toHaveCount(0); }); }); From 6f77678b65d16494f2f42c580e0e5f87f88e7886 Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Tue, 7 May 2024 15:57:09 +0300 Subject: [PATCH 16/26] test(SLB-328): update preview tests --- tests/e2e/specs/drupal/preview.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/e2e/specs/drupal/preview.spec.ts b/tests/e2e/specs/drupal/preview.spec.ts index 78865a978..4f4f0d880 100644 --- a/tests/e2e/specs/drupal/preview.spec.ts +++ b/tests/e2e/specs/drupal/preview.spec.ts @@ -10,6 +10,8 @@ test.describe('instant preview', () => { await page .getByLabel('Title', { exact: true }) .fill('Instant preview test'); + await page.locator('#edit-submit').click(); + await page.locator('li.tabs__tab a:text("Edit")').click(); await page .locator('#editor-edit-body-0-value h1 span') .first() From 225e67743cd2093424db428c4d896ef4f60c855e Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Wed, 8 May 2024 10:56:45 +0300 Subject: [PATCH 17/26] chore(SLB-328): redirect to the entity edit form after creating the entity --- .../entity_create_split.module | 36 +++++++++++++++++++ tests/e2e/specs/drupal/preview.spec.ts | 1 - 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/drupal/entity_create_split/entity_create_split.module b/packages/drupal/entity_create_split/entity_create_split.module index 2ae2e596f..a790b295c 100644 --- a/packages/drupal/entity_create_split/entity_create_split.module +++ b/packages/drupal/entity_create_split/entity_create_split.module @@ -7,6 +7,7 @@ * the rest of the fields. */ +use Drupal\Core\Entity\ContentEntityForm; use Drupal\Core\Entity\ContentEntityType; use Drupal\Core\Entity\EntityInterface; @@ -29,3 +30,38 @@ function entity_create_split_gutenberg_enabled(EntityInterface $entity) { return FALSE; } } + +/** + * Implements hook_form_alter(). + */ +function entity_create_split_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) { + $formObject = $form_state->getFormObject(); + if ($formObject instanceof ContentEntityForm && $formObject->getFormDisplay($form_state)->getMode() === 'split') { + if (empty($form['#submit'])) { + $form['#submit'] = []; + } + $form['#submit'][] = 'entity_create_split_submit_redirect'; + if (!empty($form['actions']['submit']['#submit'])) { + $form['actions']['submit']['#submit'][] = 'entity_create_split_submit_redirect'; + } + } +} + +/** + * Submit handler for the entity create from, to redirect the user to the entity + * edit form. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * + * @return void + * @throws \Drupal\Core\Entity\EntityMalformedException + */ +function entity_create_split_submit_redirect(array &$form, \Drupal\Core\Form\FormStateInterface $form_state) { + /* @var \Drupal\Core\Entity\EntityInterface $entity */ + $entity = $form_state->getFormObject()->getEntity(); + if (!empty($entity) && $entity->id()) { + $url = $entity->toUrl('edit-form'); + $form_state->setRedirectUrl($url); + } +} diff --git a/tests/e2e/specs/drupal/preview.spec.ts b/tests/e2e/specs/drupal/preview.spec.ts index 4f4f0d880..0ca958f7b 100644 --- a/tests/e2e/specs/drupal/preview.spec.ts +++ b/tests/e2e/specs/drupal/preview.spec.ts @@ -11,7 +11,6 @@ test.describe('instant preview', () => { .getByLabel('Title', { exact: true }) .fill('Instant preview test'); await page.locator('#edit-submit').click(); - await page.locator('li.tabs__tab a:text("Edit")').click(); await page .locator('#editor-edit-body-0-value h1 span') .first() From 314ce782407dfa4edbddb7cc7753519e8a597e3f Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Sat, 18 May 2024 16:05:19 +0200 Subject: [PATCH 18/26] feat(SLB-287): update naming to be compliant with backend --- ...ries.tsx => BlockImageTeasers.stories.tsx} | 8 +++---- ...ndImageCards.tsx => BlockImageTeasers.tsx} | 23 +++++++++---------- .../src/components/Organisms/PageDisplay.tsx | 4 ++-- .../ui/src/components/Routes/Page.stories.tsx | 6 ++--- packages/ui/src/tailwind.css | 9 ++++---- 5 files changed, 25 insertions(+), 25 deletions(-) rename packages/ui/src/components/Organisms/PageContent/{BlockBackgroundImageCards.stories.tsx => BlockImageTeasers.stories.tsx} (76%) rename packages/ui/src/components/Organisms/PageContent/{BlockBackgroundImageCards.tsx => BlockImageTeasers.tsx} (57%) diff --git a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.stories.tsx b/packages/ui/src/components/Organisms/PageContent/BlockImageTeasers.stories.tsx similarity index 76% rename from packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.stories.tsx rename to packages/ui/src/components/Organisms/PageContent/BlockImageTeasers.stories.tsx index 316056a66..6b90bf401 100644 --- a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.stories.tsx +++ b/packages/ui/src/components/Organisms/PageContent/BlockImageTeasers.stories.tsx @@ -4,11 +4,11 @@ import Portrait from '@stories/portrait.jpg?as=metadata'; import { Meta, StoryObj } from '@storybook/react'; import { image } from '../../../helpers/image'; -import { BlockBackgroundImageCards } from './BlockBackgroundImageCards'; +import { BlockImageTeasers } from './BlockImageTeasers'; export default { - component: BlockBackgroundImageCards, -} satisfies Meta; + component: BlockImageTeasers, +} satisfies Meta; export const Default = { args: { @@ -33,4 +33,4 @@ export const Default = { }, ], }, -} satisfies StoryObj; +} satisfies StoryObj; diff --git a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx b/packages/ui/src/components/Organisms/PageContent/BlockImageTeasers.tsx similarity index 57% rename from packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx rename to packages/ui/src/components/Organisms/PageContent/BlockImageTeasers.tsx index f3cbc828b..0b3472b57 100644 --- a/packages/ui/src/components/Organisms/PageContent/BlockBackgroundImageCards.tsx +++ b/packages/ui/src/components/Organisms/PageContent/BlockImageTeasers.tsx @@ -1,21 +1,16 @@ -import { - BlockImageTeaser, - BlockImageTeasers, - Image, - Link, -} from '@custom/schema'; +import { BlockImageTeasersFragment, Image, Link } from '@custom/schema'; import React from 'react'; import { isTruthy } from '../../../utils/isTruthy'; -export function BlockBackgroundImageCards(props: BlockImageTeasers) { +export function BlockImageTeasers(props: BlockImageTeasersFragment) { return ( // eslint-disable-next-line tailwindcss/no-custom-classname
{props.teasers.filter(isTruthy).map((teaser, index) => ( - + ))}
@@ -23,9 +18,13 @@ export function BlockBackgroundImageCards(props: BlockImageTeasers) { ); } -export function BlockBackgroundImageCard(props: BlockImageTeaser) { +// This component uses the following Flowbite component: +// https://flowbite.com/blocks/marketing/hero/#background-image-cards +export function BlockImageTeaser( + props: BlockImageTeasersFragment['teasers'][0], +) { return ( -
+
{props.image ? ( {props.title ? ( -

+

{props.title}

) : null} @@ -45,7 +44,7 @@ export function BlockBackgroundImageCard(props: BlockImageTeaser) { {props.ctaText} diff --git a/packages/ui/src/components/Organisms/PageDisplay.tsx b/packages/ui/src/components/Organisms/PageDisplay.tsx index ac0653273..9476bfc9d 100644 --- a/packages/ui/src/components/Organisms/PageDisplay.tsx +++ b/packages/ui/src/components/Organisms/PageDisplay.tsx @@ -5,11 +5,11 @@ import { isTruthy } from '../../utils/isTruthy'; import { UnreachableCaseError } from '../../utils/unreachable-case-error'; import { BreadCrumbs } from '../Molecules/Breadcrumbs'; import { PageTransition } from '../Molecules/PageTransition'; -import { BlockBackgroundImageCards } from './PageContent/BlockBackgroundImageCards'; import { BlockAccordion } from './PageContent/BlockAccordion'; import { BlockCta } from './PageContent/BlockCta'; import { BlockForm } from './PageContent/BlockForm'; import { BlockHorizontalSeparator } from './PageContent/BlockHorizontalSeparator'; +import { BlockImageTeasers } from './PageContent/BlockImageTeasers'; import { BlockImageWithText } from './PageContent/BlockImageWithText'; import { BlockMarkup } from './PageContent/BlockMarkup'; import { BlockMedia } from './PageContent/BlockMedia'; @@ -31,7 +31,7 @@ export function PageDisplay(page: PageFragment) { case 'BlockForm': return ; case 'BlockImageTeasers': - return ; + return ; case 'BlockCta': return ; case 'BlockImageWithText': diff --git a/packages/ui/src/components/Routes/Page.stories.tsx b/packages/ui/src/components/Routes/Page.stories.tsx index e28a07194..cad363277 100644 --- a/packages/ui/src/components/Routes/Page.stories.tsx +++ b/packages/ui/src/components/Routes/Page.stories.tsx @@ -11,8 +11,8 @@ import React from 'react'; import { image } from '../../helpers/image'; import { AccordionItemText } from '../Organisms/PageContent/BlockAccordion.stories'; +import { Default as BlockImageTeasers } from '../Organisms/PageContent/BlockImageTeasers.stories'; import { ImageRight } from '../Organisms/PageContent/BlockImageWithText.stories'; -import { Default as BlockBackgroundImageCards } from '../Organisms/PageContent/BlockBackgroundImageCards.stories'; import { Mixed, Paragraph } from '../Organisms/PageContent/BlockMarkup.stories'; import { WithCaption } from '../Organisms/PageContent/BlockMedia.stories'; import { Default as FrameStory } from './Frame.stories'; @@ -46,7 +46,7 @@ export const Default = { content: [ { __typename: 'BlockImageTeasers', - ...BlockBackgroundImageCards.args, + ...BlockImageTeasers.args, }, { __typename: 'BlockMarkup', @@ -70,7 +70,7 @@ export const Default = { }, { __typename: 'BlockImageTeasers', - ...BlockBackgroundImageCards.args, + ...BlockImageTeasers.args, }, ] as Exclude['content'], }, diff --git a/packages/ui/src/tailwind.css b/packages/ui/src/tailwind.css index 6c6304940..18682f9a6 100644 --- a/packages/ui/src/tailwind.css +++ b/packages/ui/src/tailwind.css @@ -36,9 +36,10 @@ content: '' !important; } -/* Selects the .block-background-image-cards only when it is the first child of the
- element immediately following the .default-hero section. +/* + Selects the .block-background-image-cards only when it is the direct + sibling of .default-hero section. */ -.default-hero + div > :first-child.block-background-image-cards { +.default-hero + .block-background-image-cards { @apply mt-2 px-0; -} \ No newline at end of file +} From 842f762a519ed38d487898f3ff70f69c9a7fb571 Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Thu, 16 May 2024 22:34:19 +0200 Subject: [PATCH 19/26] feat(SLB-393): update @headlessui/react to v2.0.0 --- packages/ui/package.json | 2 +- pnpm-lock.yaml | 116 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 110 insertions(+), 8 deletions(-) diff --git a/packages/ui/package.json b/packages/ui/package.json index 967e148ea..35560e32c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -40,7 +40,7 @@ "dependencies": { "@amazeelabs/silverback-iframe": "^1.3.0", "@custom/schema": "workspace:*", - "@headlessui/react": "^1.7.17", + "@headlessui/react": "^2.0.3", "@heroicons/react": "^2.1.1", "@hookform/resolvers": "^3.3.3", "clsx": "^2.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 642d8ebde..645740599 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -521,8 +521,8 @@ importers: specifier: workspace:* version: link:../schema '@headlessui/react': - specifier: ^1.7.17 - version: 1.7.17(react-dom@18.2.0)(react@18.2.0) + specifier: ^2.0.3 + version: 2.0.3(react-dom@18.2.0)(react@18.2.0) '@heroicons/react': specifier: ^2.1.1 version: 2.1.1(react@18.2.0) @@ -4191,6 +4191,19 @@ packages: tabbable: 6.2.0 dev: false + /@floating-ui/react@0.26.15(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-WKmfLkxTwCm09Dxq4LpjL3EPbZVSp5wvnap1jmculsfnzg2Ag/pCkP+OPyjE5dFMXqX97hsLIqJehboZ5XAHXw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) + '@floating-ui/utils': 0.2.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tabbable: 6.2.0 + dev: false + /@floating-ui/utils@0.2.1: resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} @@ -5193,14 +5206,17 @@ packages: '@hapi/hoek': 11.0.4 dev: false - /@headlessui/react@1.7.17(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==} + /@headlessui/react@2.0.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Xd1h0YZgfhxZ7W1w4TvK0/TZ1c4qaX4liYVUkAXqW1HCLcXSqnMeYAUGJS/BBroBAUL9HErjyFcRpCWRQZ/0lA==} engines: {node: '>=10'} peerDependencies: - react: ^16 || ^17 || ^18 - react-dom: ^16 || ^17 || ^18 + react: ^18 + react-dom: ^18 dependencies: - client-only: 0.0.1 + '@floating-ui/react': 0.26.15(react-dom@18.2.0)(react@18.2.0) + '@react-aria/focus': 3.17.0(react@18.2.0) + '@react-aria/interactions': 3.21.2(react@18.2.0) + '@tanstack/react-virtual': 3.5.0(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false @@ -7849,6 +7865,54 @@ packages: '@babel/runtime': 7.24.4 dev: true + /@react-aria/focus@3.17.0(react@18.2.0): + resolution: {integrity: sha512-aRzBw1WTUkcIV3xFrqPA6aB8ZVt3XyGpTaSHAypU0Pgoy2wRq9YeJYpbunsKj9CJmskuffvTqXwAjTcaQish1Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@react-aria/interactions': 3.21.2(react@18.2.0) + '@react-aria/utils': 3.24.0(react@18.2.0) + '@react-types/shared': 3.23.0(react@18.2.0) + '@swc/helpers': 0.5.11 + clsx: 2.1.0 + react: 18.2.0 + dev: false + + /@react-aria/interactions@3.21.2(react@18.2.0): + resolution: {integrity: sha512-Ju706DtoEmI/2vsfu9DCEIjDqsRBVLm/wmt2fr0xKbBca7PtmK8daajxFWz+eTq+EJakvYfLr7gWgLau9HyWXg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@react-aria/ssr': 3.9.3(react@18.2.0) + '@react-aria/utils': 3.24.0(react@18.2.0) + '@react-types/shared': 3.23.0(react@18.2.0) + '@swc/helpers': 0.5.11 + react: 18.2.0 + dev: false + + /@react-aria/ssr@3.9.3(react@18.2.0): + resolution: {integrity: sha512-5bUZ93dmvHFcmfUcEN7qzYe8yQQ8JY+nHN6m9/iSDCQ/QmCiE0kWXYwhurjw5ch6I8WokQzx66xKIMHBAa4NNA==} + engines: {node: '>= 12'} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@swc/helpers': 0.5.11 + react: 18.2.0 + dev: false + + /@react-aria/utils@3.24.0(react@18.2.0): + resolution: {integrity: sha512-JAxkPhK5fCvFVNY2YG3TW3m1nTzwRcbz7iyTSkUzLFat4N4LZ7Kzh7NMHsgeE/oMOxd8zLY+XsUxMu/E/2GujA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@react-aria/ssr': 3.9.3(react@18.2.0) + '@react-stately/utils': 3.10.0(react@18.2.0) + '@react-types/shared': 3.23.0(react@18.2.0) + '@swc/helpers': 0.5.11 + clsx: 2.1.0 + react: 18.2.0 + dev: false + /@react-dnd/asap@4.0.1: resolution: {integrity: sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==} dev: false @@ -7861,6 +7925,23 @@ packages: resolution: {integrity: sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==} dev: false + /@react-stately/utils@3.10.0(react@18.2.0): + resolution: {integrity: sha512-nji2i9fTYg65ZWx/3r11zR1F2tGya+mBubRCbMTwHyRnsSLFZaeq/W6lmrOyIy1uMJKBNKLJpqfmpT4x7rw6pg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@swc/helpers': 0.5.11 + react: 18.2.0 + dev: false + + /@react-types/shared@3.23.0(react@18.2.0): + resolution: {integrity: sha512-GQm/iPiii3ikcaMNR4WdVkJ4w0mKtV3mLqeSfSqzdqbPr6vONkqXbh3RhPlPmAJs1b4QHnexd/wZQP3U9DHOwQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /@reduxjs/toolkit@1.9.7(react-redux@7.2.9)(react@18.2.0): resolution: {integrity: sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==} peerDependencies: @@ -9309,6 +9390,12 @@ packages: legacy-swc-helpers: /@swc/helpers@0.4.14 tslib: 2.6.2 + /@swc/helpers@0.5.11: + resolution: {integrity: sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==} + dependencies: + tslib: 2.6.2 + dev: false + /@swc/jest@0.2.36(@swc/core@1.4.13): resolution: {integrity: sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==} engines: {npm: '>= 7.0.0'} @@ -9388,6 +9475,21 @@ packages: resolution: {integrity: sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==} dev: true + /@tanstack/react-virtual@3.5.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-rtvo7KwuIvqK9zb0VZ5IL7fiJAEnG+0EiFZz8FUOs+2mhGqdGmjKIaT1XU7Zq0eFqL0jonLlhbayJI/J2SA/Bw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@tanstack/virtual-core': 3.5.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@tanstack/virtual-core@3.5.0: + resolution: {integrity: sha512-KnPRCkQTyqhanNC0K63GBG3wA8I+D1fQuVnAvcBF8f13akOKeQp1gSbu6f77zCxhEk727iV5oQnbHLYzHrECLg==} + dev: false + /@testing-library/dom@9.3.4: resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==} engines: {node: '>=14'} From c9451d5efabdf182c0053747acaa639d61146d50 Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Fri, 17 May 2024 16:58:30 +0200 Subject: [PATCH 20/26] feat(SLB-393): refactor components that uses Headless UI to version 2.0 --- .../ui/src/components/Client/DesktopMenu.tsx | 36 +++++++++++-------- .../ui/src/components/Client/MobileMenu.tsx | 29 ++++++++------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/packages/ui/src/components/Client/DesktopMenu.tsx b/packages/ui/src/components/Client/DesktopMenu.tsx index dc6442c48..756031972 100644 --- a/packages/ui/src/components/Client/DesktopMenu.tsx +++ b/packages/ui/src/components/Client/DesktopMenu.tsx @@ -1,5 +1,14 @@ 'use client'; -import { Disclosure, Popover, Transition } from '@headlessui/react'; +import { + Disclosure, + DisclosureButton, + DisclosurePanel, + Popover, + PopoverButton, + PopoverGroup, + PopoverPanel, + Transition, +} from '@headlessui/react'; import { ChevronDownIcon } from '@heroicons/react/20/solid'; import clsx from 'clsx'; import React, { PropsWithChildren } from 'react'; @@ -8,9 +17,9 @@ import { avoidFocusOnClick } from '../../utils/avoidFocusOnClick'; export function DesktopMenu({ children }: PropsWithChildren<{}>) { return ( - + {children} - + ); } @@ -21,8 +30,8 @@ export function DesktopMenuDropDown({ return ( {({ open }) => ( - - + + - + {children} - + - + )} ); @@ -68,7 +74,7 @@ export function DesktopMenuDropdownDisclosure({ {({ open }) => (
- - {children} + + {children}
)}
diff --git a/packages/ui/src/components/Client/MobileMenu.tsx b/packages/ui/src/components/Client/MobileMenu.tsx index 9652da9de..022402639 100644 --- a/packages/ui/src/components/Client/MobileMenu.tsx +++ b/packages/ui/src/components/Client/MobileMenu.tsx @@ -1,5 +1,11 @@ 'use client'; -import { Dialog, Disclosure } from '@headlessui/react'; +import { + Dialog, + DialogPanel, + Disclosure, + DisclosureButton, + DisclosurePanel, +} from '@headlessui/react'; import { ChevronDownIcon } from '@heroicons/react/20/solid'; import clsx from 'clsx'; import React, { createContext, PropsWithChildren } from 'react'; @@ -11,7 +17,7 @@ const MobileMenuContext = createContext({ setIsOpen: (isOpen: boolean) => {}, }); -export function MobileMenuProvider({ children }: PropsWithChildren) { +export function MobileMenuProvider({ children }: PropsWithChildren<{}>) { const [isOpen, setIsOpen] = React.useState(false); return ( @@ -42,14 +48,15 @@ export function MobileMenuButton( ); } + export function MobileMenu({ children }: PropsWithChildren<{}>) { const { isOpen, setIsOpen } = React.useContext(MobileMenuContext); return (
- + {children} - +
); } @@ -62,13 +69,13 @@ export function MobileMenuLink({ title: string; }) { return ( - {title} - + ); } @@ -86,15 +93,13 @@ export function MobileMenuDropdown({ open && nestLevel === 2 && 'bg-gray-100 rounded mx-8', )} > - - {children} + + {children}
)} From 5313f17525ec9cf95d8be58770404973a9e36a3c Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Sat, 18 May 2024 17:32:35 +0200 Subject: [PATCH 21/26] feat(SLB-392): remove flowbite-react from dependencies --- packages/ui/package.json | 1 - pnpm-lock.yaml | 247 +++++++++++++++++++++++---------------- 2 files changed, 148 insertions(+), 100 deletions(-) diff --git a/packages/ui/package.json b/packages/ui/package.json index 35560e32c..99ff25abb 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -44,7 +44,6 @@ "@heroicons/react": "^2.1.1", "@hookform/resolvers": "^3.3.3", "clsx": "^2.1.0", - "flowbite-react": "^0.9.0", "framer-motion": "^10.17.4", "hast-util-is-element": "^2.1.3", "hast-util-select": "^5.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 645740599..d82c7daee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -532,9 +532,6 @@ importers: clsx: specifier: ^2.1.0 version: 2.1.0 - flowbite-react: - specifier: ^0.9.0 - version: 0.9.0(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.4.0) framer-motion: specifier: ^10.17.4 version: 10.17.4(react-dom@18.2.0)(react@18.2.0) @@ -604,7 +601,7 @@ importers: version: 8.0.0-alpha.14(jest@29.7.0)(vitest@1.1.1) '@storybook/test-runner': specifier: ^0.16.0 - version: 0.16.0(@types/node@20.11.17) + version: 0.16.0 '@swc/cli': specifier: ^0.1.63 version: 0.1.63(@swc/core@1.3.102) @@ -697,7 +694,7 @@ importers: version: 5.3.3 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.11.17) + version: 5.0.10 vite-imagetools: specifier: ^6.2.9 version: 6.2.9 @@ -706,7 +703,7 @@ importers: version: 1.0.3 vitest: specifier: ^1.1.1 - version: 1.1.1(@types/node@20.11.17)(happy-dom@12.10.3) + version: 1.1.1(happy-dom@12.10.3) tests/e2e: devDependencies: @@ -4178,19 +4175,6 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - /@floating-ui/react@0.26.10(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-sh6f9gVvWQdEzLObrWbJ97c0clJObiALsFe0LiR/kb3tDRKwEhObASEH2QyfdoO/ZBPzwxa9j+nYFo+sqgbioA==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - dependencies: - '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) - '@floating-ui/utils': 0.2.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - tabbable: 6.2.0 - dev: false - /@floating-ui/react@0.26.15(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-WKmfLkxTwCm09Dxq4LpjL3EPbZVSp5wvnap1jmculsfnzg2Ag/pCkP+OPyjE5dFMXqX97hsLIqJehboZ5XAHXw==} peerDependencies: @@ -5711,7 +5695,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.3.3) typescript: 5.3.3 - vite: 5.0.10(@types/node@20.11.17) + vite: 5.0.10 dev: true /@jridgewell/gen-mapping@0.3.5: @@ -7301,10 +7285,6 @@ packages: resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} dev: true - /@popperjs/core@2.11.8: - resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - dev: false - /@radix-ui/number@1.0.1: resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} dependencies: @@ -8449,7 +8429,7 @@ packages: magic-string: 0.30.9 rollup: 3.29.4 typescript: 5.3.3 - vite: 5.0.10(@types/node@20.11.17) + vite: 5.0.10 transitivePeerDependencies: - encoding - supports-color @@ -8958,7 +8938,7 @@ packages: react: 18.2.0 react-docgen: 7.0.3 react-dom: 18.2.0(react@18.2.0) - vite: 5.0.10(@types/node@20.11.17) + vite: 5.0.10 transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -9032,7 +9012,7 @@ packages: - supports-color dev: true - /@storybook/test-runner@0.16.0(@types/node@20.11.17): + /@storybook/test-runner@0.16.0: resolution: {integrity: sha512-LDmNbKFoEDW/VS9o6KR8e1r5MnbCc5ZojUfi5yqLdq80gFD7BvilgKgV0lUh/xWHryzoy+Ids5LYgrPJZmU2dQ==} engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true @@ -9052,7 +9032,7 @@ packages: commander: 9.5.0 expect-playwright: 0.8.0 glob: 10.3.12 - jest: 29.7.0(@types/node@20.11.17) + jest: 29.7.0 jest-circus: 29.7.0 jest-environment-node: 29.7.0 jest-junit: 16.0.0 @@ -9531,10 +9511,10 @@ packages: chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 - jest: 29.7.0(@types/node@20.11.17) + jest: 29.7.0 lodash: 4.17.21 redent: 3.0.0 - vitest: 1.1.1(@types/node@20.11.17)(happy-dom@12.10.3) + vitest: 1.1.1(happy-dom@12.10.3) dev: true /@testing-library/react@14.1.2(react-dom@18.2.0)(react@18.2.0): @@ -11137,7 +11117,7 @@ packages: vite: ^4 || ^5 dependencies: '@swc/core': 1.4.13 - vite: 5.0.10(@types/node@20.11.17) + vite: 5.0.10 transitivePeerDependencies: - '@swc/helpers' dev: true @@ -11164,7 +11144,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.4) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 5.0.10(@types/node@20.11.17) + vite: 5.0.10 transitivePeerDependencies: - supports-color dev: true @@ -13914,10 +13894,6 @@ packages: static-extend: 0.1.2 dev: false - /classnames@2.5.1: - resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - dev: false - /clean-deep@3.4.0: resolution: {integrity: sha512-Lo78NV5ItJL/jl+B5w0BycAisaieJGXK1qYi/9m4SjR8zbqmrUtO7Yhro40wEShGmmxs/aJLI/A+jNhdkXK8mw==} engines: {node: '>=4'} @@ -14685,7 +14661,7 @@ packages: dependencies: '@babel/runtime': 7.24.4 - /create-jest@29.7.0(@types/node@20.11.17): + /create-jest@29.7.0: resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -14694,7 +14670,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.11.17) + jest-config: 29.7.0 jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -15107,11 +15083,6 @@ packages: resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} dev: true - /debounce@2.0.0: - resolution: {integrity: sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA==} - engines: {node: '>=18'} - dev: false - /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -18709,32 +18680,6 @@ packages: engines: {node: '>=0.4.0'} dev: true - /flowbite-react@0.9.0(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.4.0): - resolution: {integrity: sha512-wRGzTPHaEuRSXiAFhdTuksezABE/AjI/iyOOBGZpsFAz/sq7zuorAqjRud9FWgy3TlFPtldl7kL93wNY2nOnKQ==} - peerDependencies: - react: '>=18' - react-dom: '>=18' - tailwindcss: ^3 - dependencies: - '@floating-ui/core': 1.6.0 - '@floating-ui/react': 0.26.10(react-dom@18.2.0)(react@18.2.0) - classnames: 2.5.1 - debounce: 2.0.0 - flowbite: 2.3.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-icons: 5.0.1(react@18.2.0) - tailwind-merge: 2.2.2 - tailwindcss: 3.4.0 - dev: false - - /flowbite@2.3.0: - resolution: {integrity: sha512-pm3JRo8OIJHGfFYWgaGpPv8E+UdWy0Z3gEAGufw+G/1dusaU/P1zoBLiQpf2/+bYAi+GBQtPVG86KYlV0W+AFQ==} - dependencies: - '@popperjs/core': 2.11.8 - mini-svg-data-uri: 1.4.4 - dev: false - /flush-write-stream@2.0.0: resolution: {integrity: sha512-uXClqPxT4xW0lcdSBheb2ObVU+kuqUk3Jk64EwieirEXZx9XUrVwp/JuBfKAWaM4T5Td/VL7QLDWPXp/MvGm/g==} dependencies: @@ -22638,7 +22583,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.11.17): + /jest-cli@29.7.0: resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -22652,10 +22597,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.11.17) + create-jest: 29.7.0 exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.11.17) + jest-config: 29.7.0 jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -22666,7 +22611,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@18.19.31): + /jest-config@29.7.0: resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -22681,7 +22626,6 @@ packages: '@babel/core': 7.24.4 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.31 babel-jest: 29.7.0(@babel/core@7.24.4) chalk: 4.1.2 ci-info: 3.9.0 @@ -22706,7 +22650,7 @@ packages: - supports-color dev: true - /jest-config@29.7.0(@types/node@20.11.17): + /jest-config@29.7.0(@types/node@18.19.31): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -22721,7 +22665,7 @@ packages: '@babel/core': 7.24.4 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 18.19.31 babel-jest: 29.7.0(@babel/core@7.24.4) chalk: 4.1.2 ci-info: 3.9.0 @@ -22884,7 +22828,7 @@ packages: jest-runner: ^29.3.1 dependencies: expect-playwright: 0.8.0 - jest: 29.7.0(@types/node@20.11.17) + jest: 29.7.0 jest-circus: 29.7.0 jest-environment-node: 29.7.0 jest-process-manager: 0.4.0 @@ -23095,7 +23039,7 @@ packages: dependencies: ansi-escapes: 6.2.1 chalk: 5.3.0 - jest: 29.7.0(@types/node@20.11.17) + jest: 29.7.0 jest-regex-util: 29.6.3 jest-watcher: 29.7.0 slash: 5.1.0 @@ -23143,7 +23087,7 @@ packages: supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.11.17): + /jest@29.7.0: resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -23156,7 +23100,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.11.17) + jest-cli: 29.7.0 transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -24865,6 +24809,7 @@ packages: /mini-svg-data-uri@1.4.4: resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true + dev: true /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -26859,6 +26804,7 @@ packages: postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 + dev: true /postcss-import@15.1.0(postcss@8.4.38): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} @@ -26893,6 +26839,7 @@ packages: dependencies: camelcase-css: 2.0.1 postcss: 8.4.32 + dev: true /postcss-js@4.0.1(postcss@8.4.38): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} @@ -26920,6 +26867,7 @@ packages: lilconfig: 3.1.1 postcss: 8.4.32 yaml: 2.3.4 + dev: true /postcss-load-config@4.0.2(postcss@8.4.38): resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} @@ -27142,6 +27090,7 @@ packages: dependencies: postcss: 8.4.32 postcss-selector-parser: 6.0.16 + dev: true /postcss-nested@6.0.1(postcss@8.4.38): resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} @@ -27483,6 +27432,7 @@ packages: nanoid: 3.3.7 picocolors: 1.0.0 source-map-js: 1.2.0 + dev: true /postcss@8.4.38: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} @@ -28298,14 +28248,6 @@ packages: source-map: 0.7.4 dev: false - /react-icons@5.0.1(react@18.2.0): - resolution: {integrity: sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==} - peerDependencies: - react: '*' - dependencies: - react: 18.2.0 - dev: false - /react-immutable-proptypes@2.2.0(immutable@3.8.2): resolution: {integrity: sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ==} peerDependencies: @@ -31103,12 +31045,6 @@ packages: - supports-color dev: false - /tailwind-merge@2.2.2: - resolution: {integrity: sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==} - dependencies: - '@babel/runtime': 7.24.4 - dev: false - /tailwindcss@3.4.0: resolution: {integrity: sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==} engines: {node: '>=14.0.0'} @@ -31138,6 +31074,7 @@ packages: sucrase: 3.35.0 transitivePeerDependencies: - ts-node + dev: true /tailwindcss@3.4.3: resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==} @@ -32803,6 +32740,27 @@ packages: - terser optional: true + /vite-node@1.1.1: + resolution: {integrity: sha512-2bGE5w4jvym5v8llF6Gu1oBrmImoNSs4WmRVcavnG2me6+8UQntTqLiAMFyiAobp+ZXhj5ZFhI7SmLiFr/jrow==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.2.8 + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite-node@1.1.1(@types/node@18.0.0): resolution: {integrity: sha512-2bGE5w4jvym5v8llF6Gu1oBrmImoNSs4WmRVcavnG2me6+8UQntTqLiAMFyiAobp+ZXhj5ZFhI7SmLiFr/jrow==} engines: {node: ^18.0.0 || >=20.0.0} @@ -32860,6 +32818,41 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true + /vite@5.0.10: + resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.19.12 + postcss: 8.4.32 + rollup: 4.14.1 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /vite@5.0.10(@types/node@18.0.0): resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -32896,8 +32889,8 @@ packages: fsevents: 2.3.3 dev: true - /vite@5.0.10(@types/node@20.11.17): - resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} + /vite@5.2.8: + resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -32924,9 +32917,8 @@ packages: terser: optional: true dependencies: - '@types/node': 20.11.17 - esbuild: 0.19.12 - postcss: 8.4.32 + esbuild: 0.20.2 + postcss: 8.4.38 rollup: 4.14.1 optionalDependencies: fsevents: 2.3.3 @@ -33347,6 +33339,63 @@ packages: - terser dev: true + /vitest@1.1.1(happy-dom@12.10.3): + resolution: {integrity: sha512-Ry2qs4UOu/KjpXVfOCfQkTnwSXYGrqTbBZxw6reIYEFjSy1QUARRg5pxiI5BEXy+kBVntxUYNMlq4Co+2vD3fQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': ^1.0.0 + '@vitest/ui': ^1.0.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@vitest/expect': 1.1.1 + '@vitest/runner': 1.1.1 + '@vitest/snapshot': 1.1.1 + '@vitest/spy': 1.1.1 + '@vitest/utils': 1.1.1 + acorn-walk: 8.3.2 + cac: 6.7.14 + chai: 4.4.1 + debug: 4.3.4 + execa: 8.0.1 + happy-dom: 12.10.3 + local-pkg: 0.5.0 + magic-string: 0.30.9 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 1.3.0 + tinybench: 2.6.0 + tinypool: 0.8.3 + vite: 5.2.8 + vite-node: 1.1.1 + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /wait-on@7.2.0: resolution: {integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==} engines: {node: '>=12.0.0'} From 4ad8b32e38cb2b0e728e6c19e57e2b2ba0e7d573 Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Sat, 18 May 2024 17:33:18 +0200 Subject: [PATCH 22/26] feat(SLB-392): refactor Accordion to use Headless UI v2.0 Disclosure --- .../Organisms/PageContent/BlockAccordion.tsx | 83 +++++++++---------- 1 file changed, 38 insertions(+), 45 deletions(-) diff --git a/packages/ui/src/components/Organisms/PageContent/BlockAccordion.tsx b/packages/ui/src/components/Organisms/PageContent/BlockAccordion.tsx index 60cde5299..994fad5dd 100644 --- a/packages/ui/src/components/Organisms/PageContent/BlockAccordion.tsx +++ b/packages/ui/src/components/Organisms/PageContent/BlockAccordion.tsx @@ -1,11 +1,18 @@ +'use client'; import { BlockAccordionFragment, Html } from '@custom/schema'; +import { + Disclosure, + DisclosureButton, + DisclosurePanel, +} from '@headlessui/react'; import { ArrowRightCircleIcon, CheckCircleIcon, + ChevronDownIcon, + ChevronUpIcon, QuestionMarkCircleIcon, } from '@heroicons/react/20/solid'; import clsx from 'clsx'; -import { Accordion, CustomFlowbiteTheme, Flowbite } from 'flowbite-react'; import type { Element } from 'hast'; import { selectAll } from 'hast-util-select'; import React, { PropsWithChildren } from 'react'; @@ -17,53 +24,39 @@ const unorderedItems: Plugin<[], Element> = () => (tree) => { }); }; -const accordionTheme: CustomFlowbiteTheme['accordion'] = { - root: { - base: 'mt-10 divide-y divide-gray-200 border-gray-200', - flush: { - off: 'last:border-0', - on: 'last:border-0', - }, - }, - content: { - base: 'pb-5 pt-5 text-base font-normal text-gray-500', - }, - title: { - base: 'flex w-full items-center justify-between p-4 pl-1 text-left font-medium text-lg text-gray-500', - flush: { - off: 'hover:bg-gray-100', - on: 'bg-transparent', - }, - heading: '', - open: { - off: '', - on: 'text-gray-900', - }, - }, -}; - -// Applying the custom theme to the Accordion component -// doesn't work out, wrapping it in a Flowbite component. -const theme: CustomFlowbiteTheme = { - accordion: accordionTheme, -}; - export function BlockAccordion(props: BlockAccordionFragment) { return ( -
+
- - - {props.items.map((item, index) => ( - - + {props.items.map((item, index) => ( + + {({ open }) => ( + <> + {item.icon && }{' '} {item.title} - - + + {open ? ( + + ) : ( + + )} + + +
{item.textContent?.markup && ( )}
-
-
- ))} -
-
+ + + )} + + ))}
From fac6beeccc87c6516727730a3ad29ec49b9d2bd8 Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Thu, 16 May 2024 22:34:19 +0200 Subject: [PATCH 23/26] feat(SLB-393): update @headlessui/react to v2.0.0 --- pnpm-lock.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d82c7daee..1a3621d5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4188,6 +4188,19 @@ packages: tabbable: 6.2.0 dev: false + /@floating-ui/react@0.26.15(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-WKmfLkxTwCm09Dxq4LpjL3EPbZVSp5wvnap1jmculsfnzg2Ag/pCkP+OPyjE5dFMXqX97hsLIqJehboZ5XAHXw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) + '@floating-ui/utils': 0.2.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tabbable: 6.2.0 + dev: false + /@floating-ui/utils@0.2.1: resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} From e95f5c967a7988d59b1397b2c8b942a6b8f13713 Mon Sep 17 00:00:00 2001 From: Mattia Simonato Date: Mon, 20 May 2024 15:11:53 +0200 Subject: [PATCH 24/26] chore: build pnpm-lock.yaml --- pnpm-lock.yaml | 194 +++++++++---------------------------------------- 1 file changed, 34 insertions(+), 160 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a3621d5f..e274343f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -601,7 +601,7 @@ importers: version: 8.0.0-alpha.14(jest@29.7.0)(vitest@1.1.1) '@storybook/test-runner': specifier: ^0.16.0 - version: 0.16.0 + version: 0.16.0(@types/node@20.11.17) '@swc/cli': specifier: ^0.1.63 version: 0.1.63(@swc/core@1.3.102) @@ -694,7 +694,7 @@ importers: version: 5.3.3 vite: specifier: ^5.0.10 - version: 5.0.10 + version: 5.0.10(@types/node@20.11.17) vite-imagetools: specifier: ^6.2.9 version: 6.2.9 @@ -703,7 +703,7 @@ importers: version: 1.0.3 vitest: specifier: ^1.1.1 - version: 1.1.1(happy-dom@12.10.3) + version: 1.1.1(@types/node@20.11.17)(happy-dom@12.10.3) tests/e2e: devDependencies: @@ -4188,19 +4188,6 @@ packages: tabbable: 6.2.0 dev: false - /@floating-ui/react@0.26.15(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-WKmfLkxTwCm09Dxq4LpjL3EPbZVSp5wvnap1jmculsfnzg2Ag/pCkP+OPyjE5dFMXqX97hsLIqJehboZ5XAHXw==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - dependencies: - '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) - '@floating-ui/utils': 0.2.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - tabbable: 6.2.0 - dev: false - /@floating-ui/utils@0.2.1: resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} @@ -5708,7 +5695,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.3.3) typescript: 5.3.3 - vite: 5.0.10 + vite: 5.0.10(@types/node@20.11.17) dev: true /@jridgewell/gen-mapping@0.3.5: @@ -8442,7 +8429,7 @@ packages: magic-string: 0.30.9 rollup: 3.29.4 typescript: 5.3.3 - vite: 5.0.10 + vite: 5.0.10(@types/node@20.11.17) transitivePeerDependencies: - encoding - supports-color @@ -8951,7 +8938,7 @@ packages: react: 18.2.0 react-docgen: 7.0.3 react-dom: 18.2.0(react@18.2.0) - vite: 5.0.10 + vite: 5.0.10(@types/node@20.11.17) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -9025,7 +9012,7 @@ packages: - supports-color dev: true - /@storybook/test-runner@0.16.0: + /@storybook/test-runner@0.16.0(@types/node@20.11.17): resolution: {integrity: sha512-LDmNbKFoEDW/VS9o6KR8e1r5MnbCc5ZojUfi5yqLdq80gFD7BvilgKgV0lUh/xWHryzoy+Ids5LYgrPJZmU2dQ==} engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true @@ -9045,7 +9032,7 @@ packages: commander: 9.5.0 expect-playwright: 0.8.0 glob: 10.3.12 - jest: 29.7.0 + jest: 29.7.0(@types/node@20.11.17) jest-circus: 29.7.0 jest-environment-node: 29.7.0 jest-junit: 16.0.0 @@ -9524,10 +9511,10 @@ packages: chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 - jest: 29.7.0 + jest: 29.7.0(@types/node@20.11.17) lodash: 4.17.21 redent: 3.0.0 - vitest: 1.1.1(happy-dom@12.10.3) + vitest: 1.1.1(@types/node@20.11.17)(happy-dom@12.10.3) dev: true /@testing-library/react@14.1.2(react-dom@18.2.0)(react@18.2.0): @@ -10485,7 +10472,6 @@ packages: typescript: 5.4.4 transitivePeerDependencies: - supports-color - dev: false /@typescript-eslint/parser@6.17.0(eslint@7.0.0)(typescript@5.3.3): resolution: {integrity: sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==} @@ -10799,7 +10785,6 @@ packages: typescript: 5.4.4 transitivePeerDependencies: - supports-color - dev: false /@typescript-eslint/typescript-estree@6.17.0(typescript@5.3.3): resolution: {integrity: sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==} @@ -11130,7 +11115,7 @@ packages: vite: ^4 || ^5 dependencies: '@swc/core': 1.4.13 - vite: 5.0.10 + vite: 5.0.10(@types/node@18.0.0) transitivePeerDependencies: - '@swc/helpers' dev: true @@ -11157,7 +11142,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.4) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 5.0.10 + vite: 5.0.10(@types/node@20.11.17) transitivePeerDependencies: - supports-color dev: true @@ -14674,7 +14659,7 @@ packages: dependencies: '@babel/runtime': 7.24.4 - /create-jest@29.7.0: + /create-jest@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -14683,7 +14668,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0 + jest-config: 29.7.0(@types/node@20.11.17) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -17211,7 +17196,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.4.4) debug: 3.2.7 eslint: 7.32.0 eslint-import-resolver-node: 0.3.9 @@ -17318,7 +17303,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.4.4) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -22596,7 +22581,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0: + /jest-cli@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -22610,10 +22595,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0 + create-jest: 29.7.0(@types/node@20.11.17) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0 + jest-config: 29.7.0(@types/node@20.11.17) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -22624,7 +22609,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0: + /jest-config@29.7.0(@types/node@18.19.31): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -22639,6 +22624,7 @@ packages: '@babel/core': 7.24.4 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 + '@types/node': 18.19.31 babel-jest: 29.7.0(@babel/core@7.24.4) chalk: 4.1.2 ci-info: 3.9.0 @@ -22663,7 +22649,7 @@ packages: - supports-color dev: true - /jest-config@29.7.0(@types/node@18.19.31): + /jest-config@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -22678,7 +22664,7 @@ packages: '@babel/core': 7.24.4 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.31 + '@types/node': 20.11.17 babel-jest: 29.7.0(@babel/core@7.24.4) chalk: 4.1.2 ci-info: 3.9.0 @@ -22841,7 +22827,7 @@ packages: jest-runner: ^29.3.1 dependencies: expect-playwright: 0.8.0 - jest: 29.7.0 + jest: 29.7.0(@types/node@20.11.17) jest-circus: 29.7.0 jest-environment-node: 29.7.0 jest-process-manager: 0.4.0 @@ -23052,7 +23038,7 @@ packages: dependencies: ansi-escapes: 6.2.1 chalk: 5.3.0 - jest: 29.7.0 + jest: 29.7.0(@types/node@20.11.17) jest-regex-util: 29.6.3 jest-watcher: 29.7.0 slash: 5.1.0 @@ -23100,7 +23086,7 @@ packages: supports-color: 8.1.1 dev: true - /jest@29.7.0: + /jest@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -23113,7 +23099,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0 + jest-cli: 29.7.0(@types/node@20.11.17) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -26879,7 +26865,7 @@ packages: dependencies: lilconfig: 3.1.1 postcss: 8.4.32 - yaml: 2.3.4 + yaml: 2.4.1 dev: true /postcss-load-config@4.0.2(postcss@8.4.38): @@ -31779,7 +31765,6 @@ packages: dependencies: tslib: 1.14.1 typescript: 5.4.4 - dev: false /tsx@4.7.1: resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==} @@ -32753,27 +32738,6 @@ packages: - terser optional: true - /vite-node@1.1.1: - resolution: {integrity: sha512-2bGE5w4jvym5v8llF6Gu1oBrmImoNSs4WmRVcavnG2me6+8UQntTqLiAMFyiAobp+ZXhj5ZFhI7SmLiFr/jrow==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.3.4 - pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.2.8 - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - /vite-node@1.1.1(@types/node@18.0.0): resolution: {integrity: sha512-2bGE5w4jvym5v8llF6Gu1oBrmImoNSs4WmRVcavnG2me6+8UQntTqLiAMFyiAobp+ZXhj5ZFhI7SmLiFr/jrow==} engines: {node: ^18.0.0 || >=20.0.0} @@ -32831,41 +32795,6 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true - /vite@5.0.10: - resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - esbuild: 0.19.12 - postcss: 8.4.32 - rollup: 4.14.1 - optionalDependencies: - fsevents: 2.3.3 - dev: true - /vite@5.0.10(@types/node@18.0.0): resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -32902,8 +32831,8 @@ packages: fsevents: 2.3.3 dev: true - /vite@5.2.8: - resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==} + /vite@5.0.10(@types/node@20.11.17): + resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -32930,8 +32859,9 @@ packages: terser: optional: true dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 + '@types/node': 20.11.17 + esbuild: 0.19.12 + postcss: 8.4.32 rollup: 4.14.1 optionalDependencies: fsevents: 2.3.3 @@ -33352,63 +33282,6 @@ packages: - terser dev: true - /vitest@1.1.1(happy-dom@12.10.3): - resolution: {integrity: sha512-Ry2qs4UOu/KjpXVfOCfQkTnwSXYGrqTbBZxw6reIYEFjSy1QUARRg5pxiI5BEXy+kBVntxUYNMlq4Co+2vD3fQ==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': ^1.0.0 - '@vitest/ui': ^1.0.0 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@vitest/expect': 1.1.1 - '@vitest/runner': 1.1.1 - '@vitest/snapshot': 1.1.1 - '@vitest/spy': 1.1.1 - '@vitest/utils': 1.1.1 - acorn-walk: 8.3.2 - cac: 6.7.14 - chai: 4.4.1 - debug: 4.3.4 - execa: 8.0.1 - happy-dom: 12.10.3 - local-pkg: 0.5.0 - magic-string: 0.30.9 - pathe: 1.1.2 - picocolors: 1.0.0 - std-env: 3.7.0 - strip-literal: 1.3.0 - tinybench: 2.6.0 - tinypool: 0.8.3 - vite: 5.2.8 - vite-node: 1.1.1 - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - /wait-on@7.2.0: resolution: {integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==} engines: {node: '>=12.0.0'} @@ -34109,6 +33982,7 @@ packages: /yaml@2.3.4: resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} engines: {node: '>= 14'} + dev: false /yaml@2.4.1: resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} From 13e5607169576df109a1c09b3e41c24a27e76741 Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Mon, 8 Apr 2024 21:50:33 +0300 Subject: [PATCH 25/26] feat(SLB-315): count in-text links references to media entities --- .../cms/config/sync/entity_usage.settings.yml | 6 +- .../sync/linkit.linkit_profile.gutenberg.yml | 2 +- packages/drupal/custom/custom.module | 71 +++++++++++++++++++ .../25086be7-ca5f-4ff8-9695-b9c71a676d4e.yml | 4 +- tests/e2e/specs/drupal/entity-usage.spec.ts | 16 +++++ 5 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 tests/e2e/specs/drupal/entity-usage.spec.ts diff --git a/apps/cms/config/sync/entity_usage.settings.yml b/apps/cms/config/sync/entity_usage.settings.yml index 575153e65..8f69040e1 100644 --- a/apps/cms/config/sync/entity_usage.settings.yml +++ b/apps/cms/config/sync/entity_usage.settings.yml @@ -5,8 +5,8 @@ local_task_enabled_entity_types: - node track_enabled_source_entity_types: - node - - content_moderation_state - block_content + - content_moderation_state - menu_link_content - media - redirect @@ -14,8 +14,8 @@ track_enabled_source_entity_types: - path_alias track_enabled_target_entity_types: - node - - content_moderation_state - block_content + - content_moderation_state - menu_link_content - media - redirect @@ -32,6 +32,8 @@ track_enabled_plugins: - html_link - ckeditor_image - block_field + - gutenberg_linked_content + - gutenberg_referenced_content - gutenberg_media_embed track_enabled_base_fields: false site_domains: { } diff --git a/apps/cms/config/sync/linkit.linkit_profile.gutenberg.yml b/apps/cms/config/sync/linkit.linkit_profile.gutenberg.yml index 844075fdc..4eded13a4 100644 --- a/apps/cms/config/sync/linkit.linkit_profile.gutenberg.yml +++ b/apps/cms/config/sync/linkit.linkit_profile.gutenberg.yml @@ -28,6 +28,6 @@ matchers: bundles: document: document group_by_bundle: 0 - substitution_type: null + substitution_type: canonical limit: '100' weight: -9 diff --git a/packages/drupal/custom/custom.module b/packages/drupal/custom/custom.module index 030c5f087..07ac7bc44 100644 --- a/packages/drupal/custom/custom.module +++ b/packages/drupal/custom/custom.module @@ -6,6 +6,8 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Render\Element; use Drupal\Core\Render\RenderContext; +use Drupal\Core\StreamWrapper\LocalStream; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\Utility\Error as ErrorUtil; use Drupal\file\Entity\File; use Drupal\media\Entity\Media; @@ -90,6 +92,75 @@ function custom_silverback_gutenberg_link_processor_outbound_url_alter( } } +/** + * Implements hook_silverback_gutenberg_link_processor_inbound_link_alter(). + * @param \DOMElement $link + * @param \Drupal\silverback_gutenberg\LinkProcessor $linkProcessor + * + * @return void + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ +function custom_silverback_gutenberg_link_processor_inbound_link_alter( + \DOMElement $link, + LinkProcessor $linkProcessor +) { + // For inbound links (when data gets saved), if the link points to a public + // file, then we want to replace the href value with "media/uuid/edit" and + // also make sure the data-id and data-entity-type attributes have the proper + // values (the uuid and the 'media' value). This is a special case for media, + // because the url of the media item is replaced by + // custom_silverback_gutenberg_link_processor_outbound_url_alter() with the + // file path, so now on inbound we need to basically do the opposite. This is + // needed so that the entity usage integration works properly (where the + // data-id and data-entity-type attributes are checked). + $href = $link->getAttribute('href'); + /* @var \Drupal\Core\StreamWrapper\StreamWrapperManager $wrapperManager */ + $wrapperManager = \Drupal::service('stream_wrapper_manager'); + /* @var \Drupal\Core\StreamWrapper\StreamWrapperInterface[] $visibleWrappers */ + $visibleWrappers = $wrapperManager->getWrappers(StreamWrapperInterface::VISIBLE); + foreach ($visibleWrappers as $scheme => $wrapperInfo) { + $wrapper = $wrapperManager->getViaScheme($scheme); + // We are only handle local streams for now. + if (!$wrapper instanceof LocalStream) { + continue; + } + if (!str_starts_with($href, '/' . $wrapper->getDirectoryPath() . '/')) { + continue; + } + // When searching for a file inside the database, the wrapper uri is used + // instead of the directory path. That is why we need the wrapper in the + // first place. + $fileuri = str_replace('/' . $wrapper->getDirectoryPath() . '/', $wrapper->getUri(), urldecode($href)); + $files = \Drupal::entityTypeManager() + ->getStorage('file') + ->loadByProperties(['uri' => $fileuri]); + // No files found, just continue to the next wrapper. + if (empty($files)) { + continue; + } + $file = array_shift($files); + $usageList = \Drupal::service('file.usage')->listUsage($file); + // If the media file usage list is empty, then this is probably some kind of + // orphan file, or tracked by some other entity type. + if (empty($usageList['file']['media'])) { + continue; + } + $mids = array_keys($usageList['file']['media']); + $mid = reset($mids); + $media = Media::load($mid); + if (empty($media)) { + continue; + } + // If we got here, we found a matching media item, so we can populate the + // link metadata with its values and just break out of the wrappers loop. + $link->setAttribute('href', '/media/' . $media->uuid() . '/edit'); + $link->setAttribute('data-id', $media->uuid()); + $link->setAttribute('data-entity-type', 'media'); + break; + } +} + /** * Implements hook_form_alter(). * diff --git a/packages/drupal/test_content/content/node/25086be7-ca5f-4ff8-9695-b9c71a676d4e.yml b/packages/drupal/test_content/content/node/25086be7-ca5f-4ff8-9695-b9c71a676d4e.yml index 5561d002f..0cceba025 100644 --- a/packages/drupal/test_content/content/node/25086be7-ca5f-4ff8-9695-b9c71a676d4e.yml +++ b/packages/drupal/test_content/content/node/25086be7-ca5f-4ff8-9695-b9c71a676d4e.yml @@ -4,6 +4,8 @@ _meta: uuid: 25086be7-ca5f-4ff8-9695-b9c71a676d4e bundle: page default_langcode: en + depends: + b998ae5e-8b56-40ca-8f80-fd4404e7e716: media default: revision_uid: - @@ -47,7 +49,7 @@ default: -

link to file

+

link to file

diff --git a/tests/e2e/specs/drupal/entity-usage.spec.ts b/tests/e2e/specs/drupal/entity-usage.spec.ts new file mode 100644 index 000000000..118ac4421 --- /dev/null +++ b/tests/e2e/specs/drupal/entity-usage.spec.ts @@ -0,0 +1,16 @@ +import { expect, test } from '@playwright/test'; + +import { cmsUrl } from '../../helpers/url'; + +test.describe('entity-usage', () => { + test.use({ storageState: '.auth/admin.json' }); + + test('media usage works with inline doc links', async ({ page }) => { + await page.goto(cmsUrl('/admin/content/media')); + page.locator('div.view-content').getByText('Example DOCX document').click(); + page.locator('a.tabs__link').getByText('Usage').click(); + await expect( + page.locator('table').getByText('Page with links'), + ).toBeVisible(); + }); +}); From 634aeea64e9ea2b1e0e1ab6a1499849d0c0aece7 Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Tue, 9 Apr 2024 09:07:16 +0300 Subject: [PATCH 26/26] test(SLB-315): update schema tests --- tests/schema/specs/links.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/schema/specs/links.spec.ts b/tests/schema/specs/links.spec.ts index f1196bc00..2262cb2b1 100644 --- a/tests/schema/specs/links.spec.ts +++ b/tests/schema/specs/links.spec.ts @@ -29,7 +29,7 @@ test('Links', async () => { { "__typename": "BlockMarkup", "markup": " -

link to file

+

link to file

link to page

",