Skip to content

Conversation

@mikeallisonJS
Copy link
Collaborator

@mikeallisonJS mikeallisonJS commented Sep 19, 2025

Summary by CodeRabbit

  • New Features

    • Added Multiselect question type: editor toolbar button, creation flows (optional submit button), inline option label editing, Add Option control, and Multiselect UI components.
  • Improvements

    • Capture, validate, submit and track multiselect responses (analytics, visitor timelines, reports); selection-limit (min/max), RTL and translations; export/filter and visitor UI support; cache/type mappings updated.
  • Tests & Docs

    • Expanded unit tests and Storybook stories across editor, canvas, properties, submission, undo/redo, and export flows.

mikeallisonJS and others added 30 commits September 3, 2025 21:27
…hemas

- Introduced MultiselectBlock and MultiselectOptionBlock types in both api-gateway and api-journeys schemas.
- Updated generated TypeScript definitions to include new block types.
- Modified canBlockHaveAction function to recognize MultiselectOptionBlock.
- Added corresponding entries in the modern API schemas and Prisma schema.
- Replaced showSelection, selectionLimit, and showProgress fields in MultiselectBlock with submitText, selectMax, and selectMin.
- Removed totalClicks field from MultiselectOptionBlock.
- Deleted the MultiselectOptionBlock resolver as it is no longer needed.
- Updated related GraphQL schemas and input types accordingly.
…es; enhance video block create and update mutations with improved error handling and input validation
…API, enhancing error handling and journey connection
…ideo block create mutation with journey ACL checks
…chema

- Changed `submitText` to `submitLabel` for better understanding.
- Renamed `selectMax` and `selectMin` to `max` and `min` respectively.
- Updated the corresponding TypeScript definitions and Prisma schema to reflect these changes.
- Added `action` field to the MultiselectBlock type.
…tiselectBlockUpdateInput

- Deleted the MultiselectBlockCreateInput type as it is no longer needed.
- Introduced the MultiselectBlockUpdateInput type with updated fields for better flexibility in block updates.
…block-modern-migration' into mikeallison/nes-546-backend-graphql-schema-and-api-for-multiselect-block
…ectBlock

- Added `multiselectBlockCreate` mutation to create new multiselect blocks with ACL checks.
- Introduced `multiselectBlockUpdate` mutation to update existing blocks, ensuring user permissions are validated.
- Updated input exports to include both `MultiselectBlockCreateInput` and `MultiselectBlockUpdateInput` for better structure.
…tBlock

- Added imports for `multiselectBlockCreate.mutation` and `multiselectBlockUpdate.mutation` to the multiselect index file, enabling the use of these mutations in the schema.
- Replaced manual field definitions for `min` and `max` in `MultiselectBlock` with `t.exposeInt` for cleaner code.
- Updated test setup for `multiselectBlockCreate` and `multiselectBlockUpdate` to include mocked return values for `min`, `max`, and `submitLabel`.
- Streamlined `MultiselectOptionBlock` field definition for `label` by removing unnecessary select options.
- Deleted `showSelection` and `showProgress` fields from the Block model in the Prisma schema to streamline the data structure.
- Introduced a new `label` field to the `MultiselectBlock` type in the GraphQL schema.
- Updated the corresponding TypeScript definitions and resolvers to ensure proper handling of the new field.
- This enhancement allows for better customization and identification of multiselect blocks within journeys.
…6-backend-graphql-schema-and-api-for-multiselect-block
- Updated the GraphQL schema to make the `label` field of the `MultiselectBlock` type non-nullable.
- Adjusted TypeScript definitions to reflect this change, ensuring that the `label` must now be provided for all multiselect blocks.
- This enhancement improves data integrity and ensures that all multiselect blocks have a defined label for better identification.
…ctBlock

- Added validation to ensure that `min` and `max` fields are non-negative and that `min` is less than or equal to `max` when both are provided.
- Updated the GraphQL mutation for creating and updating `MultiselectBlock` to handle these validations, throwing appropriate errors for invalid inputs.
- Enhanced test cases to cover scenarios where `min` and `max` are negative or where `min` exceeds `max`, ensuring robust error handling.
…eation

- Eliminated the `min` and `max` fields from the input for creating `MultiselectBlock`, defaulting them to null.
- Updated related tests to reflect the removal of these fields and ensure correct handling of null values.
- Adjusted the GraphQL mutation to accommodate these changes, simplifying the input structure for block creation.
- Updated the `multiselectBlockUpdate` mutation to ensure that the existing `max` value is preserved when it is omitted from the input.
- Adjusted related tests to verify that the `max` field is not sent in the update payload when not provided, and that the correct value is returned in the response.
- Enhanced validation logic to ensure proper handling of `min` and `max` fields during updates.
@github-actions github-actions bot temporarily deployed to Preview - watch-modern October 23, 2025 18:15 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/journeys/ui/src/components/Button/Button.tsx (1)

264-266: Critical: isEmptyForm() logic fails for non-string form values like arrays.

The type assertion as string is incorrect—formik.values is an object. More critically, the emptiness check value === '' will return false for empty arrays (e.g., multiselect with no selections), undefined, null, or numeric 0. Since this PR introduces Multiselect support, forms with empty multiselect fields will incorrectly be treated as non-empty.

Apply this diff to handle non-string values correctly:

-function isEmptyForm(): boolean {
-  return Object.values(formik.values as string).every((value) => value === '')
+function isEmptyForm(): boolean {
+  return Object.values(formik.values).every((value) => {
+    if (Array.isArray(value)) return value.length === 0
+    if (typeof value === 'string') return value === ''
+    return value == null
+  })
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bf85a78 and a3a8834.

📒 Files selected for processing (1)
  • libs/journeys/ui/src/components/Button/Button.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • libs/journeys/ui/src/components/Button/Button.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

Define a type if possible.

Files:

  • libs/journeys/ui/src/components/Button/Button.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Deploy Preview (watch, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (watch-modern, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (videos-admin, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (journeys-admin, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (journeys, 7723/merge, pull_request, 22)
  • GitHub Check: test (22, 2/3)
  • GitHub Check: test (22, 3/3)
  • GitHub Check: test (22, 1/3)
  • GitHub Check: build (22)
  • GitHub Check: lint (22)

@github-actions github-actions bot temporarily deployed to Preview - journeys October 28, 2025 16:21 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys-admin October 28, 2025 16:21 Inactive
@github-actions github-actions bot temporarily deployed to Preview - videos-admin October 28, 2025 16:21 Inactive
@github-actions github-actions bot temporarily deployed to Preview - watch-modern October 28, 2025 16:21 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
libs/journeys/ui/src/components/Button/Button.tsx (1)

271-279: Error handling for async operations is still missing.

The previous review flagged that validateForm() and submitForm() lack try-catch protection, which can lead to unhandled promise rejections if validation or submission throws. This issue remains unresolved.

Additionally, the current logic validates the form but only checks validation errors when the form is non-empty. This means users submitting an empty form with required fields won't see validation error messages, as the code exits early at line 276 before checking errors. Verify if this is the intended UX—typically, users expect to see "field required" messages even when submitting an empty form.

Apply the previously suggested fix to wrap async operations in try-catch and consider reordering checks:

 if (submitEnabled && formik != null) {
-  // Control submission flow to ensure events are recorded before navigation
   e.preventDefault()
-  const errors = await formik.validateForm(formik.values)
-
-  if (!isEmptyForm()) {
-    if (Object.keys(errors).length > 0) return
-    await formik.submitForm()
-  }
+  try {
+    const errors = await formik.validateForm(formik.values)
+    if (isEmptyForm()) return
+    if (Object.keys(errors).length > 0) return
+    await formik.submitForm()
+  } catch (error) {
+    console.error('Form submission failed:', error)
+    return
+  }
 }
🧹 Nitpick comments (1)
libs/journeys/ui/src/components/Card/Card.spec.tsx (1)

413-419: Consider clarifying the comment structure.

The code correctly implements Formik validation testing by marking fields as touched via blur events. The comment on line 415 describes the overall intent but immediately precedes only the first blur, which could be momentarily confusing.

Optionally, consider restructuring comments to align with each action:

-    const field1 = screen.getByRole('textbox', { name: 'Text Response 1*' })
-    const field2 = screen.getByRole('textbox', { name: 'Text Response 2*' })
-    // blur both required fields to mark them as touched so Formik shows errors
-    fireEvent.blur(field1)
-    fireEvent.change(field2, { target: { value: 'Test response for field 2' } })
-    // blur to mark touched so errors appear
-    fireEvent.blur(field2)
+    const field1 = screen.getByRole('textbox', { name: 'Text Response 1*' })
+    const field2 = screen.getByRole('textbox', { name: 'Text Response 2*' })
+    // Blur field1 without value to trigger required validation error
+    fireEvent.blur(field1)
+    // Fill field2 and blur to mark as touched (valid, no error expected)
+    fireEvent.change(field2, { target: { value: 'Test response for field 2' } })
+    fireEvent.blur(field2)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a3a8834 and afe54d3.

📒 Files selected for processing (3)
  • libs/journeys/ui/src/components/Button/Button.spec.tsx (5 hunks)
  • libs/journeys/ui/src/components/Button/Button.tsx (1 hunks)
  • libs/journeys/ui/src/components/Card/Card.spec.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • libs/journeys/ui/src/components/Card/Card.spec.tsx
  • libs/journeys/ui/src/components/Button/Button.tsx
  • libs/journeys/ui/src/components/Button/Button.spec.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

Define a type if possible.

Files:

  • libs/journeys/ui/src/components/Card/Card.spec.tsx
  • libs/journeys/ui/src/components/Button/Button.tsx
  • libs/journeys/ui/src/components/Button/Button.spec.tsx
🧠 Learnings (1)
📚 Learning: 2025-09-19T18:48:41.906Z
Learnt from: CR
PR: JesusFilm/core#0
File: apps/watch/AGENTS.md:0-0
Timestamp: 2025-09-19T18:48:41.906Z
Learning: Applies to apps/watch/**/*.{spec.ts,spec.tsx} : Wrap component specs with MockedProvider, VideoProvider, and WatchProvider when touching those contexts

Applied to files:

  • libs/journeys/ui/src/components/Button/Button.spec.tsx
🧬 Code graph analysis (1)
libs/journeys/ui/src/components/Button/Button.spec.tsx (2)
libs/journeys/ui/src/libs/JourneyProvider/JourneyProvider.tsx (1)
  • JourneyProvider (25-34)
libs/journeys/ui/src/components/Button/Button.tsx (1)
  • Button (92-368)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Deploy Preview (watch-modern, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (videos-admin, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (journeys, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (journeys-admin, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (watch, 7723/merge, pull_request, 22)
  • GitHub Check: test (22, 2/3)
  • GitHub Check: build (22)
  • GitHub Check: test (22, 1/3)
  • GitHub Check: test (22, 3/3)
  • GitHub Check: lint (22)
🔇 Additional comments (6)
libs/journeys/ui/src/components/Button/Button.spec.tsx (3)

285-293: LGTM! Test correctly mocks Formik's submitForm method.

The test properly adapts to the updated Button component behavior where submitForm is called directly instead of relying on handleSubmit.

Also applies to: 313-313


980-990: LGTM! JourneyProvider wrapper provides necessary context.

Wrapping the Button with JourneyProvider correctly supplies the journey context needed for action handling and variant-specific behavior in the admin variant test.


1244-1277: LGTM! Async test properly verifies form submission flow.

The test correctly:

  • Mocks both validateForm and submitForm methods
  • Uses waitFor to verify async operations complete
  • Verifies the submission flow matches the Button component's behavior
libs/journeys/ui/src/components/Card/Card.spec.tsx (3)

439-448: LGTM! Improved validation assertion pattern.

The change from getByText to getAllByText(...).length > 0 makes the test more robust by not assuming an exact count of error messages. The blur event on line 447 correctly ensures Formik's touched state is updated before re-submission, following proper Formik testing patterns.


533-537: LGTM! Proper email validation test setup.

The code correctly simulates user interaction by entering an invalid email value and blurring the field to trigger Formik's email validation. This follows the same improved pattern used in the required field test.


551-560: LGTM! Consistent validation pattern for email field.

The validation assertion and subsequent valid email entry follow the same improved pattern as the required field test. The use of getAllByText(...).length > 0 is more robust, and the blur event on line 560 ensures proper Formik state management before form re-submission.

@github-actions github-actions bot temporarily deployed to Preview - watch-modern October 28, 2025 16:32 Inactive
@github-actions github-actions bot temporarily deployed to Preview - videos-admin October 28, 2025 16:32 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys-admin October 28, 2025 16:32 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys October 28, 2025 16:32 Inactive
@github-actions github-actions bot temporarily deployed to Preview - videos-admin October 29, 2025 20:54 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys-admin October 29, 2025 20:54 Inactive
@github-actions github-actions bot temporarily deployed to Preview - watch-modern October 29, 2025 20:54 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys October 29, 2025 20:54 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
libs/locales/en/apps-journeys-admin.json (1)

267-267: Add missing period for consistency with similar guidance strings.

Line 267 is missing a period at the end, while the equivalent poll guidance at line 278 ("To edit poll content, choose each option individually.") includes one.

Apply this diff to maintain consistency:

- "To edit multiselect content, choose each option individually": "To edit multiselect content, choose each option individually",
+ "To edit multiselect content, choose each option individually.": "To edit multiselect content, choose each option individually.",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between eb391af and ba5c0ea.

⛔ Files ignored due to path filters (28)
  • apps/journeys-admin/__generated__/BlockDuplicate.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/BlockFields.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/BlockRestore.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/CardCtaRestore.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/CardFormRestore.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/CardIntroRestore.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/CardPollRestore.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/CardQuoteRestore.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/GetAdminJourney.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/GetAdminJourneyWithPlausibleToken.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/GetJourney.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/GetPublisherTemplate.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/JourneyFields.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/MenuBlockRestore.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/StepBlockRestoreFromAction.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/StepBlockRestoreFromSocialPreview.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/StepBlockRestoreFromStep.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/StepDuplicate.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/TextResponseWithButtonRestore.ts is excluded by !**/__generated__/**
  • apps/journeys/__generated__/BlockFields.ts is excluded by !**/__generated__/**
  • apps/journeys/__generated__/GetJourney.ts is excluded by !**/__generated__/**
  • apps/journeys/__generated__/JourneyFields.ts is excluded by !**/__generated__/**
  • apps/watch/__generated__/BlockFields.ts is excluded by !**/__generated__/**
  • apps/watch/__generated__/GetJourney.ts is excluded by !**/__generated__/**
  • apps/watch/__generated__/JourneyFields.ts is excluded by !**/__generated__/**
  • libs/journeys/ui/src/libs/JourneyProvider/__generated__/JourneyFields.ts is excluded by !**/__generated__/**
  • libs/journeys/ui/src/libs/block/__generated__/BlockFields.ts is excluded by !**/__generated__/**
  • libs/journeys/ui/src/libs/useJourneyQuery/__generated__/GetJourney.ts is excluded by !**/__generated__/**
📒 Files selected for processing (3)
  • apps/journeys-admin/src/components/Editor/Slider/Content/Canvas/Canvas.stories.tsx (1 hunks)
  • libs/journeys/ui/src/components/BlockRenderer/BlockRenderer.spec.tsx (2 hunks)
  • libs/locales/en/apps-journeys-admin.json (5 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • apps/journeys-admin/src/components/Editor/Slider/Content/Canvas/Canvas.stories.tsx
  • libs/journeys/ui/src/components/BlockRenderer/BlockRenderer.spec.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

Define a type if possible.

Files:

  • apps/journeys-admin/src/components/Editor/Slider/Content/Canvas/Canvas.stories.tsx
  • libs/journeys/ui/src/components/BlockRenderer/BlockRenderer.spec.tsx
apps/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/apps.mdc)

apps/**/*.{js,jsx,ts,tsx}: Always use MUI over HTML elements; avoid using CSS or tags.
Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.

Files:

  • apps/journeys-admin/src/components/Editor/Slider/Content/Canvas/Canvas.stories.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: mikeallisonJS
PR: JesusFilm/core#7629
File: apis/api-journeys/schema.graphql:947-956
Timestamp: 2025-09-10T19:52:52.934Z
Learning: The canBlockHaveAction function in apis/api-journeys/src/app/modules/action/canBlockHaveAction/canBlockHaveAction.ts incorrectly includes 'MultiselectOptionBlock' in its allowlist, but MultiselectOptionBlock blocks should not have actions. Only MultiselectBlock (the container) should have actions, not the individual option blocks.
Learnt from: mikeallisonJS
PR: JesusFilm/core#7629
File: apis/api-journeys/schema.graphql:947-956
Timestamp: 2025-09-10T19:52:52.934Z
Learning: In multiselect block architecture, MultiselectBlock is the parent container that can have actions (similar to RadioQuestionBlock), while MultiselectOptionBlock represents individual selectable options without actions (similar to RadioOptionBlock). Actions are handled at the container level, not on individual option blocks.
📚 Learning: 2025-02-04T16:36:58.743Z
Learnt from: Kneesal
PR: JesusFilm/core#5100
File: apps/api-journeys-modern/src/schema/blocks/card/card.zod.ts:0-0
Timestamp: 2025-02-04T16:36:58.743Z
Learning: In the CardBlockSchema (apps/api-journeys-modern/src/schema/blocks/card/card.zod.ts), the parentBlockId field should remain nullable, despite the description indicating it can only be a child of a step block.

Applied to files:

  • apps/journeys-admin/src/components/Editor/Slider/Content/Canvas/Canvas.stories.tsx
📚 Learning: 2025-09-10T19:52:52.934Z
Learnt from: mikeallisonJS
PR: JesusFilm/core#7629
File: apis/api-journeys/schema.graphql:947-956
Timestamp: 2025-09-10T19:52:52.934Z
Learning: The canBlockHaveAction function in apis/api-journeys/src/app/modules/action/canBlockHaveAction/canBlockHaveAction.ts incorrectly includes 'MultiselectOptionBlock' in its allowlist, but MultiselectOptionBlock blocks should not have actions. Only MultiselectBlock (the container) should have actions, not the individual option blocks.

Applied to files:

  • apps/journeys-admin/src/components/Editor/Slider/Content/Canvas/Canvas.stories.tsx
  • libs/journeys/ui/src/components/BlockRenderer/BlockRenderer.spec.tsx
📚 Learning: 2025-09-10T19:52:52.934Z
Learnt from: mikeallisonJS
PR: JesusFilm/core#7629
File: apis/api-journeys/schema.graphql:947-956
Timestamp: 2025-09-10T19:52:52.934Z
Learning: In multiselect block architecture, MultiselectBlock is the parent container that can have actions (similar to RadioQuestionBlock), while MultiselectOptionBlock represents individual selectable options without actions (similar to RadioOptionBlock). Actions are handled at the container level, not on individual option blocks.

Applied to files:

  • apps/journeys-admin/src/components/Editor/Slider/Content/Canvas/Canvas.stories.tsx
  • libs/journeys/ui/src/components/BlockRenderer/BlockRenderer.spec.tsx
📚 Learning: 2025-09-12T20:30:23.683Z
Learnt from: mikeallisonJS
PR: JesusFilm/core#7658
File: libs/locales/ms-MY/apps-watch.json:62-62
Timestamp: 2025-09-12T20:30:23.683Z
Learning: Interpolation spacing and localization fixes for i18n files are specific to journeys-admin context and may not apply to other apps like apps-watch.

Applied to files:

  • libs/locales/en/apps-journeys-admin.json
🧬 Code graph analysis (1)
libs/journeys/ui/src/components/BlockRenderer/BlockRenderer.spec.tsx (1)
libs/journeys/ui/src/components/BlockRenderer/BlockRenderer.tsx (1)
  • BlockRenderer (163-317)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Deploy Preview (watch-modern, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (journeys, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (videos-admin, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (watch, 7723/merge, pull_request, 22)
  • GitHub Check: Deploy Preview (journeys-admin, 7723/merge, pull_request, 22)
  • GitHub Check: lint (22)
🔇 Additional comments (7)
libs/locales/en/apps-journeys-admin.json (2)

390-391: Verify naming consistency with Poll block naming pattern.

The Poll block uses "Poll Block Selected" (line 388) and "Poll Option Properties" (line 389), but Multiselect uses "Multiselect Properties" (line 390) and "Multiselect Option Properties" (line 391).

Consider aligning the naming: should line 390 be "Multiselect Block Selected" for consistency, or is "Multiselect Properties" intentionally different for UI reasons?


162-162: Multiselect i18n strings look well-structured.

The remaining new translation keys follow established patterns and naming conventions throughout the file. Strings are clear, properly capitalized, and contextually well-placed alongside related block types (Poll, Button, etc.). The selection limit/range terminology (Min/Max selections) is intuitive.

Also applies to: 268-274, 390-391, 690-690, 715-715

libs/journeys/ui/src/components/BlockRenderer/BlockRenderer.spec.tsx (5)

8-11: LGTM!

The imports are properly structured and follow the established pattern for block type imports.


452-483: LGTM!

The wrapper test properly verifies the hierarchy and containment of the MultiselectOption with both general and specific wrappers, consistent with the RadioOption wrapper test pattern.


485-512: LGTM!

The test correctly renders MultiselectQuestion with children. The use of SnackbarProvider is appropriate since MultiselectQuestion has min and max validation constraints that likely require user notifications, consistent with the SignUp component which also uses SnackbarProvider.


514-550: LGTM!

The wrapper test properly verifies the MultiselectQuestion hierarchy, button group structure, and CSS classes. The test pattern is consistent with the RadioQuestion wrapper test and provides good coverage.


435-450: Review comment is incorrect.

The test is correct as written. The type definition for BlockFields_MultiselectOptionBlock does not include an action property—it only contains __typename, id, parentBlockId, parentOrder, and label. This differs by design from RadioOptionBlock, which does include an action property. The test correctly reflects the type structure, with no need to add action: null.

Likely an incorrect or invalid review comment.

@tanflem tanflem self-requested a review October 29, 2025 21:57
@mikeallisonJS mikeallisonJS added this pull request to the merge queue Oct 29, 2025
Merged via the queue into main with commit 0eaa366 Oct 29, 2025
35 checks passed
@mikeallisonJS mikeallisonJS deleted the mikeallison/nes-796-multiselect-block-frontend branch October 29, 2025 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants