diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 06cfe2bac..bd2280348 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -35,10 +35,6 @@ runs: shell: bash run: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 pnpm i - - name: Check formatting - shell: bash - run: pnpm test:format - - name: Cache playwright binaries uses: actions/cache@v3 id: playwright-cache diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9e9897278..83587e7ac 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -23,11 +23,15 @@ jobs: - name: Extract branch name shell: bash - run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT + run: + echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> + $GITHUB_OUTPUT id: extract_branch - name: Prepare deployment package - run: pnpm turbo:prep && pnpm deploy --filter "@custom/website" ../deploy --prod + run: + pnpm turbo:prep && pnpm deploy --filter "@custom/website" ../deploy + --prod env: VITE_DECAP_REPO: ${{ github.repository }} VITE_DECAP_BRANCH: ${{ steps.extract_branch.outputs.branch }} @@ -54,7 +58,10 @@ jobs: - name: Deploy to dev run: pnpm netlify deploy --prod --filter "@custom/website" working-directory: ../deploy - if: github.ref == 'refs/heads/dev' && steps.netlify-check.outputs.available == 'true' && vars.NETLIFY_DEV_ID != '' + if: + github.ref == 'refs/heads/dev' && + steps.netlify-check.outputs.available == 'true' && vars.NETLIFY_DEV_ID + != '' env: NETLIFY_SITE_ID: ${{ vars.NETLIFY_DEV_ID }} NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} @@ -62,8 +69,10 @@ jobs: - name: Deploy to prod run: pnpm netlify deploy --prod --filter "@custom/website" working-directory: ../deploy - if: github.ref == 'refs/heads/prod' && steps.netlify-check.outputs.available == 'true' && vars.NETLIFY_PROD_ID != '' + if: + github.ref == 'refs/heads/prod' && + steps.netlify-check.outputs.available == 'true' && + vars.NETLIFY_PROD_ID != '' env: NETLIFY_SITE_ID: ${{ vars.NETLIFY_PROD_ID }} NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - diff --git a/.github/workflows/high_content_volume.yml b/.github/workflows/high_content_volume.yml index 02edab017..88bbda211 100644 --- a/.github/workflows/high_content_volume.yml +++ b/.github/workflows/high_content_volume.yml @@ -35,7 +35,9 @@ jobs: run: pnpm --filter "@custom/website" clean - name: 'Drupal: Create content' - run: pnpm --filter "@custom/cms" drush php:script scripts/create-lots-of-content/run.php + run: + pnpm --filter "@custom/cms" drush php:script + scripts/create-lots-of-content/run.php - name: 'Gatsby: Full build' run: pnpm --filter "@custom/website" build:gatsby @@ -44,7 +46,9 @@ jobs: run: pnpm --filter "@custom/website" build:gatsby - name: 'Drupal: Create more content' - run: pnpm --filter "@custom/cms" drush php:script scripts/create-lots-of-content/create-100-pages.php + run: + pnpm --filter "@custom/cms" drush php:script + scripts/create-lots-of-content/create-100-pages.php - name: 'Gatsby: Incremental build with new content' run: pnpm --filter "@custom/website" build:gatsby diff --git a/.github/workflows/merge_dev_to_stage.yml b/.github/workflows/merge_dev_to_stage.yml index 49741615b..55ca2f714 100644 --- a/.github/workflows/merge_dev_to_stage.yml +++ b/.github/workflows/merge_dev_to_stage.yml @@ -18,4 +18,4 @@ jobs: with: type: now target_branch: stage - github_token: ${{ github.token }} \ No newline at end of file + github_token: ${{ github.token }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a8f39a873..9c1f520d6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,9 @@ jobs: steps: - name: Init check if: ${{ github.repository != 'AmazeeLabs/silverback-template'}} - run: echo 'Please run the INIT script. See the root README.md for instructions.' && false + run: + echo 'Please run the INIT script. See the root README.md for + instructions.' && false - name: Checkout uses: actions/checkout@v3 @@ -19,6 +21,9 @@ jobs: - name: Setup uses: ./.github/actions/setup + - name: Check formatting + run: pnpm test:format + - name: TurboRepo local server uses: felixmosh/turborepo-gh-artifacts@v2 with: @@ -59,6 +64,8 @@ jobs: storybookBaseDir: packages/ui onlyChanged: true exitOnceUploaded: true + externals: | + static/stories/webforms/** if: ${{ steps.chromatic-check.outputs.available == 'true' }} - name: Deploy storybook to netlify @@ -75,7 +82,9 @@ jobs: - name: Merge release to prod (silverback-template only) uses: devmasx/merge-branch@1.4.0 - if: ${{ github.repository == 'AmazeeLabs/silverback-template' && github.ref == 'refs/heads/release'}} + if: + ${{ github.repository == 'AmazeeLabs/silverback-template' && + github.ref == 'refs/heads/release'}} with: type: now from_branch: release @@ -84,7 +93,9 @@ jobs: docker_build: name: Docker Build - if: startsWith(github.ref_name, 'test-all/') || startsWith(github.head_ref, 'test-all/') + if: + startsWith(github.ref_name, 'test-all/') || startsWith(github.head_ref, + 'test-all/') runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/test_without_turbo_cache.yml b/.github/workflows/test_without_turbo_cache.yml index 837d24c68..757b757c0 100644 --- a/.github/workflows/test_without_turbo_cache.yml +++ b/.github/workflows/test_without_turbo_cache.yml @@ -3,11 +3,16 @@ on: workflow_dispatch: jobs: - test: name: Test runs-on: ubuntu-20.04 steps: + - name: Init check + if: ${{ github.repository != 'AmazeeLabs/silverback-template'}} + run: + echo 'Please run the INIT script. See the root README.md for + instructions.' && false + - name: Checkout uses: actions/checkout@v3 with: @@ -16,8 +21,18 @@ jobs: - name: Setup uses: ./.github/actions/setup + - name: TurboRepo local server + uses: felixmosh/turborepo-gh-artifacts@v2 + with: + server-token: 'local' + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Test - run: pnpm turbo:test + run: pnpm turbo:test:force + env: + TURBO_API: 'http://127.0.0.1:9080' + TURBO_TOKEN: 'local' + TURBO_TEAM: 'local' - name: Upload Playwright report uses: actions/upload-artifact@v3 diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 913f8189c..7061f94f0 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -28,8 +28,3 @@ RUN chmod a+x phpactor.phar RUN sudo mv phpactor.phar /usr/local/bin/phpactor # Install gh cli RUN sudo install-packages gh - -RUN curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/latest/download/lazygit_0.40.2_Linux_x86_64.tar.gz" \ - && tar xf lazygit.tar.gz lazygit \ - && sudo install lazygit /usr/local/bin - diff --git a/.gitpod.yml b/.gitpod.yml index b8f8aab49..ccd012215 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,6 +1,6 @@ tasks: - name: Setup - init: pnpm install && pnpm turbo build --no-daemon --go-fallback && gp sync-done setup + init: pnpm install && pnpm turbo build gp sync-done setup env: NETLIFY_URL: http://localhost:8000 DRUPAL_EXTERNAL_URL: http://localhost:8888 diff --git a/.idea/prettier.xml b/.idea/prettier.xml index 6e16fd106..50edf175d 100644 --- a/.idea/prettier.xml +++ b/.idea/prettier.xml @@ -3,6 +3,6 @@ \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 6ad8630f6..95e9082a1 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,4 @@ apps packages tests .turbo +pnpm-lock.yaml diff --git a/apps/cms/.prettierignore b/apps/cms/.prettierignore new file mode 100644 index 000000000..d851b213c --- /dev/null +++ b/apps/cms/.prettierignore @@ -0,0 +1,2 @@ +config/sync +composer.lock diff --git a/apps/cms/composer.json b/apps/cms/composer.json index bb6feeb43..50427ad02 100644 --- a/apps/cms/composer.json +++ b/apps/cms/composer.json @@ -100,6 +100,9 @@ }, "amazeelabs/silverback_gatsby": { "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" } }, "patchLevel": { 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/apps/cms/patches/.gitkeep b/apps/cms/patches/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/cms/scaffold/all.services.yml b/apps/cms/scaffold/all.services.yml index d1e0a45c9..6eb08a2b2 100644 --- a/apps/cms/scaffold/all.services.yml +++ b/apps/cms/scaffold/all.services.yml @@ -3,10 +3,10 @@ # To activate this feature, follow the instructions at the top of the # 'example.settings.local.php' file, which sits next to this file. parameters: - # Configure Cross-Site HTTP requests (CORS). - # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS - # for more information about the topic in general. - # Note: By default the configuration is disabled. + # Configure Cross-Site HTTP requests (CORS). + # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS + # for more information about the topic in general. + # Note: By default the configuration is disabled. cors.config: enabled: true # Specify allowed headers, like 'x-allowed-header'. diff --git a/apps/website/netlify/functions/github-proxy.mts b/apps/website/netlify/functions/github-proxy.mts index f7a7e2008..d47302636 100644 --- a/apps/website/netlify/functions/github-proxy.mts +++ b/apps/website/netlify/functions/github-proxy.mts @@ -5,6 +5,9 @@ export default function (request: Request, context: Context) { if (!process.env.DECAP_GITHUB_TOKEN) { throw new Error('Missing environment variable DECAP_GITHUB_TOKEN.'); } - return githubProxy(request, process.env.DECAP_GITHUB_TOKEN, '/.netlify/functions/github-proxy'); + return githubProxy( + request, + process.env.DECAP_GITHUB_TOKEN, + '/.netlify/functions/github-proxy', + ); } - diff --git a/apps/website/publisher.config.ts b/apps/website/publisher.config.ts index abaf98f01..6c5d95797 100644 --- a/apps/website/publisher.config.ts +++ b/apps/website/publisher.config.ts @@ -16,7 +16,7 @@ export default defineConfig({ // cannot report it. // Workaround: Do a double build on the first build. 'if test -d public; then echo "Single build" && pnpm build:gatsby; else echo "Double build" && pnpm build:gatsby && pnpm build:gatsby; fi' - : 'pnpm build:gatsby', + : 'DRUPAL_EXTERNAL_URL=http://127.0.0.1:8888 pnpm build:gatsby', outputTimeout: 1000 * 60 * 10, }, clean: 'pnpm clean', diff --git a/docker-compose.yml b/docker-compose.yml index c5ad2e374..663f65444 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,12 @@ version: '2.3' x-volumes: - &default-volumes - # Define all volumes you would like to have real-time mounted into the docker containers + &default-volumes # Define all volumes you would like to have real-time mounted into the docker containers volumes: - ./packages/drupal:/app/web/modules/custom:delegated - ./apps/cms:/app:delegated -x-environment: - &default-environment +x-environment: &default-environment LAGOON_PROJECT: &lagoon-project ${COMPOSE_PROJECT_NAME:-slbtemplate} # Route that should be used locally, if you are using pygmy, this route *must* end with .docker.amazee.io LAGOON_ROUTE: &default-url http://${COMPOSE_PROJECT_NAME:-slbtemplate}.docker.amazee.io @@ -17,8 +15,7 @@ x-environment: # See example.docker-compose.override.yml for XDEBUG_ENABLE option x-user: - &default-user - # The default user under which the containers should run. Change this if you are on linux and run with another user than id `1000` + &default-user # The default user under which the containers should run. Change this if you are on linux and run with another user than id `1000` user: '1000' services: @@ -58,7 +55,7 @@ services: depends_on: - cli # basically just tells docker-compose to build the cli first environment: - << : *default-environment # loads the defined environment variables from the top + <<: *default-environment # loads the defined environment variables from the top networks: - amazeeio-network - default diff --git a/package.json b/package.json index e4b531976..c86941856 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,11 @@ "tb": "pnpm turbo --filter @custom/website", "test:format": "pnpm test:format:root --check && pnpm test:format:workspaces --check", "test:format:fix": "pnpm test:format:root --write && pnpm test:format:workspaces --write", - "test:format:root": "pnpm prettier '**/*.{js,cjs,mjs,ts,jsx,tsx,gql,graphql,graphqls,md,mdx,json,htm,html}' --ignore-path='./.prettierignore'", - "test:format:workspaces": "pnpm --workspace-concurrency=1 -r exec prettier '**/*.{js,cjs,mjs,ts,jsx,tsx,gql,graphql,graphqls,md,mdx,json,htm,html}' --ignore-path='./.gitignore'", + "test:format:root": "pnpm prettier --ignore-unknown '**/**'", + "test:format:workspaces": "pnpm --workspace-concurrency=1 -r exec prettier --ignore-unknown '**/**'", "turbo:local": "if [ -z $CI ]; then echo $(date)$RANDOM > apps/cms/turbo-seed.txt; fi", "turbo:test": "pnpm turbo:local && pnpm tb test:unit --no-daemon --go-fallback --output-logs=new-only && pnpm tb test:integration --no-daemon --go-fallback --output-logs=new-only --concurrency=1", + "turbo:test:force": "pnpm tb test:unit --no-daemon --go-fallback --output-logs=new-only --force && pnpm tb test:integration --no-daemon --go-fallback --output-logs=new-only --concurrency=1 --force", "turbo:test:quick": "pnpm turbo:local && pnpm tb test:unit --no-daemon --go-fallback --output-logs=new-only", "turbo:prep": "pnpm turbo:local && pnpm tb prep --no-daemon --go-fallback --output-logs=new-only", "turbo:prep:force": "rm -f apps/cms/web/sites/default/files/.sqlite && pnpm tb prep --no-daemon --go-fallback --force", @@ -37,7 +38,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-simple-import-sort": "^10.0.0", "husky": "^8.0.3", - "prettier": "^3.1.1", + "prettier": "^3.2.5", "turbo": "^1.11.2", "typescript": "^5.3.3", "vitest": "^1.1.1" diff --git a/packages/drupal/entity_create_split/README.md b/packages/drupal/entity_create_split/README.md new file mode 100644 index 000000000..12a79a2d2 --- /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. + +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 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.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..a790b295c --- /dev/null +++ b/packages/drupal/entity_create_split/entity_create_split.module @@ -0,0 +1,67 @@ +setFormClass('split', $entity_type->getFormClass('default')); + } + } +} + +/** + * Implements hook_gutenberg_enabled(). + */ +function entity_create_split_gutenberg_enabled(EntityInterface $entity) { + if (isset($entity->disableGutenberg) && $entity->disableGutenberg === TRUE) { + 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/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..e5762db49 --- /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_callback: '\Drupal\entity_create_split\Controller\EntityCreateSplitController::getTitle' + requirements: + _custom_access: '\Drupal\entity_create_split\Controller\EntityCreateSplitController::access' + 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..4216b1192 --- /dev/null +++ b/packages/drupal/entity_create_split/entity_create_split.services.yml @@ -0,0 +1,9 @@ +services: + 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 new file mode 100644 index 000000000..f27b6b2fa --- /dev/null +++ b/packages/drupal/entity_create_split/src/Controller/EntityCreateSplitController.php @@ -0,0 +1,61 @@ +entityTypeManager()->getDefinition($entity_type); + $bundleKey = $entityTypeDefinition->getKey('bundle'); + $entity = $this->entityTypeManager()->getStorage($entity_type)->create([$bundleKey => $bundle]); + $entity->disableGutenberg = TRUE; + $editForm = $this->entityTypeManager()->getFormObject($entity_type, 'split')->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); + } +} 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/gutenberg_blocks/css/edit.css b/packages/drupal/gutenberg_blocks/css/edit.css index 39ab66473..1ef6f0086 100644 --- a/packages/drupal/gutenberg_blocks/css/edit.css +++ b/packages/drupal/gutenberg_blocks/css/edit.css @@ -1,10 +1,10 @@ /* We can put any editor specific styling in here */ .gutenberg__editor .editor-styles-wrapper { - font-family:inherit; + font-family: inherit; } .gutenberg__editor blockquote { - margin:0; + margin: 0; } .gutenberg__editor blockquote .quote-image img { @@ -40,4 +40,63 @@ left: 0; transform-origin: 0 0; transform: rotate(90deg); -} \ No newline at end of file +} + +.gutenberg__editor ul li { + line-height: 1.5rem; +} +.gutenberg__editor .list-style--arrows li, +.gutenberg__editor .list-style--checkmarks li, +.gutenberg__editor .list-style--question-marks li { + list-style-type: none; + position: relative; + padding-left: 0.625rem; + margin-left: 0; + line-height: 1.25rem !important; +} + +.gutenberg__editor .list-style--arrows li::before { + position: absolute; + left: -1.25rem; + line-height: 1.25rem; + height: 1.25rem; + vertical-align: middle; + content: url("data:image/svg+xml,%3Csvg aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='%236B7280' %3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM6.75 9.25a.75.75 0 0 0 0 1.5h4.59l-2.1 1.95a.75.75 0 0 0 1.02 1.1l3.5-3.25a.75.75 0 0 0 0-1.1l-3.5-3.25a.75.75 0 1 0-1.02 1.1l2.1 1.95H6.75Z' clip-rule='evenodd'%3E%3C/path%3E%3C/svg%3E"); +} + +.gutenberg__editor .list-style--checkmarks li::before { + position: absolute; + left: -1.125rem; + line-height: 1.25rem; + height: 1.25rem; + vertical-align: middle; + margin-top: 0.5px; + content: url("data:image/svg+xml,%3Csvg width='18' height='19' viewBox='0 0 18 19' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect y='0.5' width='18' height='18' rx='9' fill='%23DEF7EC'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M13.6947 6.20519C13.8259 6.33646 13.8996 6.51448 13.8996 6.70009C13.8996 6.88571 13.8259 7.06372 13.6947 7.19499L8.0947 12.795C7.96343 12.9262 7.78541 12.9999 7.5998 12.9999C7.41418 12.9999 7.23617 12.9262 7.1049 12.795L4.3049 9.99499C4.17739 9.86297 4.10683 9.68615 4.10842 9.50261C4.11002 9.31908 4.18364 9.14351 4.31342 9.01372C4.44321 8.88394 4.61878 8.81032 4.80232 8.80872C4.98585 8.80713 5.16268 8.87768 5.2947 9.00519L7.5998 11.3103L12.7049 6.20519C12.8362 6.07397 13.0142 6.00024 13.1998 6.00024C13.3854 6.00024 13.5634 6.07397 13.6947 6.20519Z' fill='%230E9F6E'/%3E%3C/svg%3E "); +} + +.gutenberg__editor .list-style--question-marks li::before { + position: absolute; + left: -1.125rem; + vertical-align: middle; + line-height: 1.25rem; + height: 1.25rem; + content: url("data:image/svg+xml,%3Csvg aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='%236B7280' viewBox='0 0 20 24'%3E%3Cpath fill-rule='evenodd' d='M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm9.008-3.018a1.502 1.502 0 0 1 2.522 1.159v.024a1.44 1.44 0 0 1-1.493 1.418 1 1 0 0 0-1.037.999V14a1 1 0 1 0 2 0v-.539a3.44 3.44 0 0 0 2.529-3.256 3.502 3.502 0 0 0-7-.255 1 1 0 0 0 2 .076c.014-.398.187-.774.48-1.044Zm.982 7.026a1 1 0 1 0 0 2H12a1 1 0 1 0 0-2h-.01Z' clip-rule='evenodd'/%3E%3C/svg%3E%0A"); +} +.gutenberg__editor .custom-block-accordion-item-text.questionmark h3::before { + margin-right: 7px; + content: url("data:image/svg+xml,%3Csvg aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='%236B7280' viewBox='0 0 20 24'%3E%3Cpath fill-rule='evenodd' d='M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm9.008-3.018a1.502 1.502 0 0 1 2.522 1.159v.024a1.44 1.44 0 0 1-1.493 1.418 1 1 0 0 0-1.037.999V14a1 1 0 1 0 2 0v-.539a3.44 3.44 0 0 0 2.529-3.256 3.502 3.502 0 0 0-7-.255 1 1 0 0 0 2 .076c.014-.398.187-.774.48-1.044Zm.982 7.026a1 1 0 1 0 0 2H12a1 1 0 1 0 0-2h-.01Z' clip-rule='evenodd'/%3E%3C/svg%3E%0A"); +} + +.gutenberg__editor .custom-block-accordion-item-text.checkmark h3::before { + margin-right: 7px; + content: url("data:image/svg+xml,%3Csvg aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='%236B7280' viewBox='0 0 20 24'%3E%3Cpath fill-rule='evenodd' d='M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm13.707-1.293a1 1 0 0 0-1.414-1.414L11 12.586l-1.793-1.793a1 1 0 0 0-1.414 1.414l2.5 2.5a1 1 0 0 0 1.414 0l4-4Z' clip-rule='evenodd'%3E%3C/path%3E%3C/svg%3E"); +} + +.gutenberg__editor .custom-block-accordion-item-text.arrow h3::before { + margin-right: 7px; + content: url("data:image/svg+xml,%3Csvg aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='%236B7280' %3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM6.75 9.25a.75.75 0 0 0 0 1.5h4.59l-2.1 1.95a.75.75 0 0 0 1.02 1.1l3.5-3.25a.75.75 0 0 0 0-1.1l-3.5-3.25a.75.75 0 1 0-1.02 1.1l2.1 1.95H6.75Z' clip-rule='evenodd'%3E%3C/path%3E%3C/svg%3E"); +} + +.gutenberg__editor .edit-post-visual-editor__content-area a.no-underline { + text-decoration: none; +} diff --git a/packages/drupal/gutenberg_blocks/gutenberg_blocks.gutenberg.yml b/packages/drupal/gutenberg_blocks/gutenberg_blocks.gutenberg.yml index 8ed456c02..42c51a1e5 100644 --- a/packages/drupal/gutenberg_blocks/gutenberg_blocks.gutenberg.yml +++ b/packages/drupal/gutenberg_blocks/gutenberg_blocks.gutenberg.yml @@ -3,4 +3,4 @@ theme-support: typography: dropCap: false libraries-edit: - - gutenberg_blocks/edit \ No newline at end of file + - gutenberg_blocks/edit diff --git a/packages/drupal/gutenberg_blocks/gutenberg_blocks.info.yml b/packages/drupal/gutenberg_blocks/gutenberg_blocks.info.yml index 039c68d3e..193f1a006 100644 --- a/packages/drupal/gutenberg_blocks/gutenberg_blocks.info.yml +++ b/packages/drupal/gutenberg_blocks/gutenberg_blocks.info.yml @@ -5,4 +5,4 @@ package: Custom core_version_requirement: ^9 || ^10 dependencies: - gutenberg:gutenberg - - silverback_gutenberg:silverback_gutenberg \ No newline at end of file + - silverback_gutenberg:silverback_gutenberg diff --git a/packages/drupal/gutenberg_blocks/gutenberg_blocks.libraries.yml b/packages/drupal/gutenberg_blocks/gutenberg_blocks.libraries.yml index 89370de15..87a7ad6d9 100644 --- a/packages/drupal/gutenberg_blocks/gutenberg_blocks.libraries.yml +++ b/packages/drupal/gutenberg_blocks/gutenberg_blocks.libraries.yml @@ -13,4 +13,3 @@ customisations: /gutenberg.css: { preprocess: false } dependencies: - core/drupalSettings - diff --git a/packages/drupal/gutenberg_blocks/src/Plugin/Validation/GutenbergValidator/AccordionItemTextValidator.php b/packages/drupal/gutenberg_blocks/src/Plugin/Validation/GutenbergValidator/AccordionItemTextValidator.php new file mode 100644 index 000000000..4ab6b31ab --- /dev/null +++ b/packages/drupal/gutenberg_blocks/src/Plugin/Validation/GutenbergValidator/AccordionItemTextValidator.php @@ -0,0 +1,51 @@ + [ + 'field_label' => $this->t('Title'), + 'rules' => ['required'], + ], + ]; + } + + /** + * {@inheritDoc} + */ + public function validateContent($block = []): array { + $expectedChildren = [ + 'validationType' => GutenbergCardinalityValidatorInterface::CARDINALITY_ANY, + 'min' => 1, + 'max' => GutenbergCardinalityValidatorInterface::CARDINALITY_UNLIMITED, + ]; + return $this->validateCardinality($block, $expectedChildren); + } + +} diff --git a/packages/drupal/gutenberg_blocks/src/Plugin/Validation/GutenbergValidator/AccordionValidator.php b/packages/drupal/gutenberg_blocks/src/Plugin/Validation/GutenbergValidator/AccordionValidator.php new file mode 100644 index 000000000..d0bae38b0 --- /dev/null +++ b/packages/drupal/gutenberg_blocks/src/Plugin/Validation/GutenbergValidator/AccordionValidator.php @@ -0,0 +1,42 @@ + 'custom/accordion-item-text', + 'blockLabel' => $this->t('Accordion'), + 'min' => 1, + 'max' => GutenbergCardinalityValidatorInterface::CARDINALITY_UNLIMITED, + ], + ]; + return $this->validateCardinality($block, $expectedChildren); + } + +} diff --git a/packages/drupal/gutenberg_blocks/src/blocks/accordion-item-text.tsx b/packages/drupal/gutenberg_blocks/src/blocks/accordion-item-text.tsx new file mode 100644 index 000000000..aaf3f4a6d --- /dev/null +++ b/packages/drupal/gutenberg_blocks/src/blocks/accordion-item-text.tsx @@ -0,0 +1,91 @@ +import clsx from 'clsx'; +import React, { Fragment } from 'react'; +import { + InnerBlocks, + InspectorControls, + RichText, +} from 'wordpress__block-editor'; +import { registerBlockType } from 'wordpress__blocks'; +import { PanelBody, SelectControl } from 'wordpress__components'; +import { compose, withState } from 'wordpress__compose'; + +// @ts-ignore +const { t: __ } = Drupal; +// @ts-ignore +registerBlockType('custom/accordion-item-text', { + title: 'Accordion Item Text', + icon: 'text', + category: 'layout', + parent: ['custom/accordion'], + attributes: { + title: { + type: 'string', + }, + icon: { + type: 'string', + }, + }, + // @ts-ignore + edit: compose(withState())((props) => { + const { attributes, setAttributes } = props; + const icons = [ + { label: __('- Select an optional icon -'), value: '' }, + { label: __('Checkmark'), value: 'checkmark' }, + { label: __('Questionmark'), value: 'questionmark' }, + { label: __('Arrow'), value: 'arrow' }, + ]; + setAttributes({ + icon: attributes.icon === undefined ? '' : attributes.icon, + }); + + return ( + + + + { + setAttributes({ + icon: newValue, + }); + }} + /> + + +
+
{__('Accordion Item Text')}
+
+ { + setAttributes({ title: newValue }); + }} + /> + +
+
+
+ ); + }), + + save: () => { + return ; + }, +}); diff --git a/packages/drupal/gutenberg_blocks/src/blocks/accordion.tsx b/packages/drupal/gutenberg_blocks/src/blocks/accordion.tsx new file mode 100644 index 000000000..924746dd8 --- /dev/null +++ b/packages/drupal/gutenberg_blocks/src/blocks/accordion.tsx @@ -0,0 +1,25 @@ +import { InnerBlocks } from 'wordpress__block-editor'; +import { registerBlockType } from 'wordpress__blocks'; + +// @ts-ignore +const { t: __ } = Drupal; + +registerBlockType('custom/accordion', { + title: __('Accordion'), + icon: 'menu', + category: 'layout', + attributes: {}, + edit: () => { + return ( +
+
{__('Accordion')}
+ +
+ ); + }, + save: () => , +}); diff --git a/packages/drupal/gutenberg_blocks/src/blocks/cta.tsx b/packages/drupal/gutenberg_blocks/src/blocks/cta.tsx index c0bbaacdc..5b70375d3 100644 --- a/packages/drupal/gutenberg_blocks/src/blocks/cta.tsx +++ b/packages/drupal/gutenberg_blocks/src/blocks/cta.tsx @@ -1,3 +1,4 @@ +import clsx from 'clsx'; import { // @ts-ignore __experimentalLinkControl as LinkControl, @@ -5,7 +6,7 @@ import { RichText, } from 'wordpress__block-editor'; import { registerBlockType } from 'wordpress__blocks'; -import { PanelBody, ToggleControl } from 'wordpress__components'; +import { PanelBody, SelectControl, ToggleControl } from 'wordpress__components'; import { compose, withState } from 'wordpress__compose'; // @ts-ignore @@ -14,6 +15,23 @@ const { t: __ } = Drupal; // @ts-ignore const { setPlainTextAttribute } = silverbackGutenbergUtils; +const ArrowRightIcon = () => ( + + + +); + // @ts-ignore registerBlockType('custom/cta', { title: 'CTA', @@ -37,28 +55,46 @@ registerBlockType('custom/cta', { openInNewTab: { type: 'boolean', }, + icon: { + type: 'string', + default: 'NONE', + }, + iconPosition: { + type: 'string', + default: 'AFTER', + }, }, // @ts-ignore edit: compose(withState({}))((props) => { return (
- { - setPlainTextAttribute(props, 'text', text); - }} - /> + + { + setPlainTextAttribute(props, 'text', text); + }} + /> + {typeof props.attributes.icon !== 'undefined' && + props.attributes.icon === 'ARROW' && } + + { + props.setAttributes({ + icon, + }); + }} + /> + {typeof props.attributes.icon !== 'undefined' && + props.attributes.icon !== 'NONE' && ( + { + props.setAttributes({ + iconPosition, + }); + }} + /> + )}
diff --git a/packages/drupal/gutenberg_blocks/src/blocks/horizontal-separator.tsx b/packages/drupal/gutenberg_blocks/src/blocks/horizontal-separator.tsx new file mode 100644 index 000000000..720094b1f --- /dev/null +++ b/packages/drupal/gutenberg_blocks/src/blocks/horizontal-separator.tsx @@ -0,0 +1,21 @@ +import { registerBlockType } from 'wordpress__blocks'; +import { compose, withState } from 'wordpress__compose'; + +declare const Drupal: { t: (s: string) => string }; + +const { t: __ } = Drupal; + +// @ts-ignore +registerBlockType(`custom/horizontal-separator`, { + title: __('Horizontal separator'), + icon: 'minus', + category: 'text', + attributes: {}, + // @ts-ignore + edit: compose(withState())(() => { + return
; + }), + save() { + return null; + }, +}); diff --git a/packages/drupal/gutenberg_blocks/src/index.ts b/packages/drupal/gutenberg_blocks/src/index.ts index 2763bdad8..e2c0159f7 100644 --- a/packages/drupal/gutenberg_blocks/src/index.ts +++ b/packages/drupal/gutenberg_blocks/src/index.ts @@ -9,3 +9,6 @@ import './blocks/image-with-text'; import './filters/list'; import './blocks/cta'; import './blocks/quote'; +import './blocks/horizontal-separator'; +import './blocks/accordion'; +import './blocks/accordion-item-text'; diff --git a/packages/drupal/test_content/.prettierignore b/packages/drupal/test_content/.prettierignore new file mode 100644 index 000000000..dd0f08c94 --- /dev/null +++ b/packages/drupal/test_content/.prettierignore @@ -0,0 +1,2 @@ +content +webforms 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 ebd66747b..465669703 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 @@ -55,6 +55,8 @@ default:

A standalone paragraph with markup and link

+ + @@ -74,9 +76,6 @@ default:

Heading

- -
12
34
Caption
-

Quote

Citation
@@ -111,15 +110,30 @@ default: - + + + - + + -

+

Incididunt laborum velit non proident nostrud velit. Minim excepteur ut aliqua nisi. Culpa laboris consectetur proident. Tempor esse ullamco et dolor proident id officia laborum voluptate nostrud elit dolore qui amet. Ex Lorem irure eu anim ipsum officia.

+ + + + + + + + + +

Incididunt laborum velit non proident nostrud velit. Minim excepteur ut aliqua nisi. Culpa laboris consectetur proident. Tempor esse ullamco et dolor proident id officia laborum voluptate nostrud elit dolore qui amet. Ex Lorem irure eu anim ipsum officia.

+ + format: gutenberg summary: '' diff --git a/packages/drupal/test_content/webforms/styling.yml b/packages/drupal/test_content/webforms/styling.yml index 7cfdd2291..b73dbb9c2 100644 --- a/packages/drupal/test_content/webforms/styling.yml +++ b/packages/drupal/test_content/webforms/styling.yml @@ -25,11 +25,17 @@ elements: |- Foo: Foo Bar: 'Bar -- Bar has a description.' Baz: Baz - textfield: - '#type': textfield - '#title': Textfield - '#description': 'Textfield description.' - '#placeholder': 'Placeholder goes here' + flexbox: + '#type': webform_flexbox + textfield: + '#type': textfield + '#title': 'First name' + '#description': 'Textfield description.' + '#placeholder': 'Placeholder goes here' + '#required': true + last_name: + '#type': textfield + '#title': 'Last name' email_with_a_conformation: '#type': webform_email_confirm '#title': 'Email with a confirmation' @@ -56,6 +62,22 @@ elements: |- Foo: Foo Bar: Bar Baz: Baz + terms_of_service: + '#type': webform_terms_of_service + '#title': 'I agree to the {terms of service} in modal' + '#required': true + '#terms_content': |- + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a felis convallis, pharetra mauris a, eleifend justo. Sed massa ipsum, suscipit sit amet varius a, dignissim at ex. Integer euismod a sapien id auctor. Etiam dignissim scelerisque nunc in porttitor. Nam rutrum molestie mauris, a bibendum ligula. Etiam eget posuere enim. Quisque id condimentum ligula. + + Duis sit amet sapien justo. Suspendisse potenti. Aliquam venenatis congue quam, quis eleifend tellus. Duis aliquam sollicitudin purus sed rutrum. Nullam vitae ante lorem. Curabitur malesuada nibh lectus, ut ultricies elit mattis sed. Ut pharetra urna nec lacus pellentesque vestibulum. Nulla turpis sem, aliquam finibus pharetra at, interdum sit amet ligula. In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce ullamcorper justo in risus condimentum suscipit. Nam in nunc sit amet neque fringilla suscipit. Sed nec metus tempus, rhoncus dolor et, vehicula urna. Donec ultricies leo pharetra ante vulputate vulputate. Nullam tincidunt pharetra nisi eu faucibus. Duis efficitur, massa eget sagittis sodales, magna dolor dapibus justo, et facilisis tellus augue et est. + terms_of_service_01: + '#type': webform_terms_of_service + '#title': 'I agree to the {terms of service} in slideout' + '#terms_type': slideout + '#terms_content': |- + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a felis convallis, pharetra mauris a, eleifend justo. Sed massa ipsum, suscipit sit amet varius a, dignissim at ex. Integer euismod a sapien id auctor. Etiam dignissim scelerisque nunc in porttitor. Nam rutrum molestie mauris, a bibendum ligula. Etiam eget posuere enim. Quisque id condimentum ligula. + + Duis sit amet sapien justo. Suspendisse potenti. Aliquam venenatis congue quam, quis eleifend tellus. Duis aliquam sollicitudin purus sed rutrum. Nullam vitae ante lorem. Curabitur malesuada nibh lectus, ut ultricies elit mattis sed. Ut pharetra urna nec lacus pellentesque vestibulum. Nulla turpis sem, aliquam finibus pharetra at, interdum sit amet ligula. In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce ullamcorper justo in risus condimentum suscipit. Nam in nunc sit amet neque fringilla suscipit. Sed nec metus tempus, rhoncus dolor et, vehicula urna. Donec ultricies leo pharetra ante vulputate vulputate. Nullam tincidunt pharetra nisi eu faucibus. Duis efficitur, massa eget sagittis sodales, magna dolor dapibus justo, et facilisis tellus augue et est. actions: '#type': webform_actions '#title': 'Submit button(s)' diff --git a/packages/schema/src/fragments/Page.gql b/packages/schema/src/fragments/Page.gql index cbf2227eb..59285ccc4 100644 --- a/packages/schema/src/fragments/Page.gql +++ b/packages/schema/src/fragments/Page.gql @@ -35,6 +35,8 @@ fragment Page on Page { ...BlockCta ...BlockImageWithText ...BlockQuote + ...BlockHorizontalSeparator + ...BlockAccordion } metaTags { tag diff --git a/packages/schema/src/fragments/PageContent/BlockAccordion.gql b/packages/schema/src/fragments/PageContent/BlockAccordion.gql new file mode 100644 index 000000000..0f77b257d --- /dev/null +++ b/packages/schema/src/fragments/PageContent/BlockAccordion.gql @@ -0,0 +1,13 @@ +fragment BlockAccordion on BlockAccordion { + items { + ...BlockAccordionItemText + } +} + +fragment BlockAccordionItemText on BlockAccordionItemText { + title + icon + textContent { + markup + } +} diff --git a/packages/schema/src/fragments/PageContent/BlockCta.gql b/packages/schema/src/fragments/PageContent/BlockCta.gql index 2dae0fda2..db56f5580 100644 --- a/packages/schema/src/fragments/PageContent/BlockCta.gql +++ b/packages/schema/src/fragments/PageContent/BlockCta.gql @@ -2,4 +2,6 @@ fragment BlockCta on BlockCta { url text openInNewTab + icon + iconPosition } diff --git a/packages/schema/src/fragments/PageContent/BlockHorizontalSeparator.gql b/packages/schema/src/fragments/PageContent/BlockHorizontalSeparator.gql new file mode 100644 index 000000000..ef992a139 --- /dev/null +++ b/packages/schema/src/fragments/PageContent/BlockHorizontalSeparator.gql @@ -0,0 +1,5 @@ +fragment BlockHorizontalSeparator on BlockHorizontalSeparator { + # This is not really used, but until we have other real setting fields, we + # have to provide a dummy one. + markup +} diff --git a/packages/schema/src/fragments/PageContent/BlockImageWithText.gql b/packages/schema/src/fragments/PageContent/BlockImageWithText.gql index b026e8676..84da846c2 100644 --- a/packages/schema/src/fragments/PageContent/BlockImageWithText.gql +++ b/packages/schema/src/fragments/PageContent/BlockImageWithText.gql @@ -1,9 +1,10 @@ fragment BlockImageWithText on BlockImageWithText { image { - source(width: 1536, sizes: [[768, 768], [1536, 1536]]) + source(width: 1536, height: 1336) alt } textContent { markup } + imagePosition } diff --git a/packages/schema/src/schema.graphql b/packages/schema/src/schema.graphql index ca52d0ef3..fbeafa4e3 100644 --- a/packages/schema/src/schema.graphql +++ b/packages/schema/src/schema.graphql @@ -199,13 +199,15 @@ type Hero { } union PageContent @resolveEditorBlockType = - BlockMarkup + | BlockMarkup | BlockMedia | BlockForm | BlockImageTeasers | BlockCta | BlockImageWithText | BlockQuote + | BlockHorizontalSeparator + | BlockAccordion type BlockForm @type(id: "custom/form") { url: Url @resolveEditorBlockAttribute(key: "formId") @webformIdToUrl(id: "$") @@ -245,10 +247,33 @@ type BlockImageTeaser @default @value { ctaUrl: Url @resolveEditorBlockAttribute(key: "ctaUrl") } +type BlockAccordion @type(id: "custom/accordion") { + items: [BlockAccordionItemText!]! @resolveEditorBlockChildren +} + +type BlockAccordionItemText @default @value { + title: String! @resolveEditorBlockAttribute(key: "title") + icon: String! @resolveEditorBlockAttribute(key: "icon") + textContent: BlockMarkup @resolveEditorBlockChildren @seek(pos: 0) +} + type BlockCta @type(id: "custom/cta") { url: Url @resolveEditorBlockAttribute(key: "url") text: String @resolveEditorBlockAttribute(key: "text") openInNewTab: Boolean @resolveEditorBlockAttribute(key: "openInNewTab") + icon: CTAIconType @resolveEditorBlockAttribute(key: "icon") + iconPosition: CTAIconPosition + @resolveEditorBlockAttribute(key: "iconPosition") +} + +enum CTAIconType { + NONE + ARROW +} + +enum CTAIconPosition { + AFTER + BEFORE } type BlockImageWithText @type(id: "custom/image-with-text") { @@ -270,6 +295,10 @@ type BlockQuote @type(id: "custom/quote") { image: MediaImage @resolveEditorBlockMedia } +type BlockHorizontalSeparator @type(id: "custom/horizontal-separator") { + markup: Markup! @resolveEditorBlockMarkup +} + input PaginationInput { limit: Int! offset: Int! diff --git a/packages/ui/package.json b/packages/ui/package.json index f3320b5ce..967e148ea 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -44,6 +44,7 @@ "@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/packages/ui/src/components/Atoms/Container.css b/packages/ui/src/components/Atoms/Container.css index 8985bc6dc..566f31114 100644 --- a/packages/ui/src/components/Atoms/Container.css +++ b/packages/ui/src/components/Atoms/Container.css @@ -1,5 +1,5 @@ .container-page { - @apply max-w-full px-[1.25rem] md:px-[3.75rem]; + @apply max-w-full px-[1.25rem] md:px-[3.75rem] lg:px-[5rem]; } .container-content { @@ -7,7 +7,7 @@ } .container-text { - @apply max-w-[40.75rem] xl:ml-[11rem] lg:ml-[7rem]; + @apply max-w-3xl mx-auto; } .nested-container .container-page { @@ -27,7 +27,7 @@ } .nested-container .container-page:first-child .container-text, -.nested-container .container-page:first-child .container-text *:first-child{ +.nested-container .container-page:first-child .container-text *:first-child { @apply mt-0; } .nested-container .container-page:last-child .container-text, @@ -35,6 +35,7 @@ @apply mb-0; } -.container-nested .prose ul:not(ul ul), .container-nested .prose ol:not(ol ol) { +.container-nested .prose ul:not(ul ul), +.container-nested .prose ol:not(ol ol) { @apply mt-0; } diff --git a/packages/ui/src/components/Atoms/List.css b/packages/ui/src/components/Atoms/List.css new file mode 100644 index 000000000..f185321e6 --- /dev/null +++ b/packages/ui/src/components/Atoms/List.css @@ -0,0 +1,49 @@ +ul li { + @apply !text-base !font-medium !text-gray-900; +} + +ul { + @apply !pl-4; +} + +.list-style--arrows li, +.list-style--checkmarks li, +.list-style--question-marks li { + list-style-type: none; + position: relative; + padding-left: 0.625rem; + margin-left: 0; + line-height: 1.25rem !important; +} + +ul li::marker { + @apply !text-gray-500; +} + +.list-style--arrows li::before { + position: absolute; + left: -1.25rem; + line-height: 1.25rem; + height: 1.25rem; + vertical-align: middle; + content: url("data:image/svg+xml,%3Csvg aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='%236B7280' %3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM6.75 9.25a.75.75 0 0 0 0 1.5h4.59l-2.1 1.95a.75.75 0 0 0 1.02 1.1l3.5-3.25a.75.75 0 0 0 0-1.1l-3.5-3.25a.75.75 0 1 0-1.02 1.1l2.1 1.95H6.75Z' clip-rule='evenodd'%3E%3C/path%3E%3C/svg%3E"); +} + +.list-style--checkmarks li::before { + position: absolute; + left: -1.125rem; + line-height: 1.25rem; + height: 1.25rem; + vertical-align: middle; + margin-top: 0.5px; + content: url("data:image/svg+xml,%3Csvg width='18' height='19' viewBox='0 0 18 19' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect y='0.5' width='18' height='18' rx='9' fill='%23DEF7EC'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M13.6947 6.20519C13.8259 6.33646 13.8996 6.51448 13.8996 6.70009C13.8996 6.88571 13.8259 7.06372 13.6947 7.19499L8.0947 12.795C7.96343 12.9262 7.78541 12.9999 7.5998 12.9999C7.41418 12.9999 7.23617 12.9262 7.1049 12.795L4.3049 9.99499C4.17739 9.86297 4.10683 9.68615 4.10842 9.50261C4.11002 9.31908 4.18364 9.14351 4.31342 9.01372C4.44321 8.88394 4.61878 8.81032 4.80232 8.80872C4.98585 8.80713 5.16268 8.87768 5.2947 9.00519L7.5998 11.3103L12.7049 6.20519C12.8362 6.07397 13.0142 6.00024 13.1998 6.00024C13.3854 6.00024 13.5634 6.07397 13.6947 6.20519Z' fill='%230E9F6E'/%3E%3C/svg%3E "); +} + +.list-style--question-marks li::before { + position: absolute; + left: -1.125rem; + vertical-align: middle; + line-height: 1.25rem; + height: 1.25rem; + content: url("data:image/svg+xml,%3Csvg aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='%236B7280' viewBox='0 0 20 24'%3E%3Cpath fill-rule='evenodd' d='M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm9.008-3.018a1.502 1.502 0 0 1 2.522 1.159v.024a1.44 1.44 0 0 1-1.493 1.418 1 1 0 0 0-1.037.999V14a1 1 0 1 0 2 0v-.539a3.44 3.44 0 0 0 2.529-3.256 3.502 3.502 0 0 0-7-.255 1 1 0 0 0 2 .076c.014-.398.187-.774.48-1.044Zm.982 7.026a1 1 0 1 0 0 2H12a1 1 0 1 0 0-2h-.01Z' clip-rule='evenodd'/%3E%3C/svg%3E%0A"); +} diff --git a/packages/ui/src/components/Molecules/Breadcrumbs.stories.tsx b/packages/ui/src/components/Molecules/Breadcrumbs.stories.tsx new file mode 100644 index 000000000..9a7c6d0e5 --- /dev/null +++ b/packages/ui/src/components/Molecules/Breadcrumbs.stories.tsx @@ -0,0 +1,24 @@ +import { FrameQuery, OperationExecutor } from '@custom/schema'; +import { Meta } from '@storybook/react'; +import React from 'react'; + +import { Default as FrameStory } from '../Routes/Frame.stories'; +import { BreadCrumbs } from './Breadcrumbs'; + +export default { + component: BreadCrumbs, + parameters: { + layout: 'fullscreen', + location: new URL('local:/gatsby-turbo'), + }, +} satisfies Meta; + +export const Default = { + render: () => { + return ( + + + + ); + }, +}; diff --git a/packages/ui/src/components/Molecules/Breadcrumbs.tsx b/packages/ui/src/components/Molecules/Breadcrumbs.tsx new file mode 100644 index 000000000..431291137 --- /dev/null +++ b/packages/ui/src/components/Molecules/Breadcrumbs.tsx @@ -0,0 +1,56 @@ +import { Link } from '@custom/schema'; +import { ChevronRightIcon } from '@heroicons/react/24/outline'; +import clsx from 'clsx'; +import React from 'react'; + +import { isTruthy } from '../../utils/isTruthy'; +import { useBreadcrumbs } from '../Routes/Menu'; + +export function BreadCrumbs() { + const breadcrumbs = useBreadcrumbs(); + + if (!breadcrumbs.length) { + return null; + } + + return ( +
+ +
+ ); +} diff --git a/packages/ui/src/components/Organisms/Footer.tsx b/packages/ui/src/components/Organisms/Footer.tsx index c41d751be..73dc34bdf 100644 --- a/packages/ui/src/components/Organisms/Footer.tsx +++ b/packages/ui/src/components/Organisms/Footer.tsx @@ -21,11 +21,13 @@ export function Footer() { return (