-
Notifications
You must be signed in to change notification settings - Fork 13
feat: multiselect block frontend #7723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: multiselect block frontend #7723
Conversation
…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
…schema definitions
…updatevideoblock-modern-migration
…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.
There was a problem hiding this 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 stringis incorrect—formik.valuesis an object. More critically, the emptiness checkvalue === ''will return false for empty arrays (e.g., multiselect with no selections),undefined,null, or numeric0. 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.
📒 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)
…d improve test coverage
There was a problem hiding this 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()andsubmitForm()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.
📒 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.tsxlibs/journeys/ui/src/components/Button/Button.tsxlibs/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.tsxlibs/journeys/ui/src/components/Button/Button.tsxlibs/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'ssubmitFormmethod.The test properly adapts to the updated Button component behavior where
submitFormis called directly instead of relying onhandleSubmit.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
validateFormandsubmitFormmethods- Uses
waitForto 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
getByTexttogetAllByText(...).length > 0makes 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 > 0is more robust, and the blur event on line 560 ensures proper Formik state management before form re-submission.
There was a problem hiding this 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.
⛔ Files ignored due to path filters (28)
apps/journeys-admin/__generated__/BlockDuplicate.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/BlockFields.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/BlockRestore.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/CardCtaRestore.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/CardFormRestore.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/CardIntroRestore.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/CardPollRestore.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/CardQuoteRestore.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/GetAdminJourney.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/GetAdminJourneyWithPlausibleToken.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/GetJourney.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/GetPublisherTemplate.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/JourneyFields.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/MenuBlockRestore.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/StepBlockRestoreFromAction.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/StepBlockRestoreFromSocialPreview.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/StepBlockRestoreFromStep.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/StepDuplicate.tsis excluded by!**/__generated__/**apps/journeys-admin/__generated__/TextResponseWithButtonRestore.tsis excluded by!**/__generated__/**apps/journeys/__generated__/BlockFields.tsis excluded by!**/__generated__/**apps/journeys/__generated__/GetJourney.tsis excluded by!**/__generated__/**apps/journeys/__generated__/JourneyFields.tsis excluded by!**/__generated__/**apps/watch/__generated__/BlockFields.tsis excluded by!**/__generated__/**apps/watch/__generated__/GetJourney.tsis excluded by!**/__generated__/**apps/watch/__generated__/JourneyFields.tsis excluded by!**/__generated__/**libs/journeys/ui/src/libs/JourneyProvider/__generated__/JourneyFields.tsis excluded by!**/__generated__/**libs/journeys/ui/src/libs/block/__generated__/BlockFields.tsis excluded by!**/__generated__/**libs/journeys/ui/src/libs/useJourneyQuery/__generated__/GetJourney.tsis 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.tsxlibs/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.tsxlibs/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.tsxlibs/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.tsxlibs/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
minandmaxvalidation 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_MultiselectOptionBlockdoes not include anactionproperty—it only contains__typename,id,parentBlockId,parentOrder, andlabel. This differs by design fromRadioOptionBlock, which does include anactionproperty. The test correctly reflects the type structure, with no need to addaction: null.Likely an incorrect or invalid review comment.
Summary by CodeRabbit
New Features
Improvements
Tests & Docs