feat: Randomize row order matrix questions#3233
Conversation
|
@SaiSawant1 is attempting to deploy a commit to the formbricks Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughThe changes introduce a new feature for matrix questions in surveys, allowing users to manage the order of question options through a shuffle mechanism. This includes the addition of a shuffle option in the Changes
Assessment against linked issues
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
Thank you for following the naming conventions for pull request titles! 🙏 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (5)
packages/surveys/src/lib/utils.ts (2)
22-34: LGTM: NewgetShuffledRowsfunction implemented correctly.The new
getShuffledRowsfunction correctly implements the shuffling logic based on the providedshuffleOption. It handles both "all" and "exceptLast" cases as expected.Consider extracting the shuffling logic for the "exceptLast" case into a separate function for improved readability:
const shuffleExceptLast = (array: any[]) => { const lastElement = array.pop(); if (lastElement) { shuffle(array); array.push(lastElement); } }; export const getShuffledRows = (rows: TI18nString[], shuffleOption: TShuffleOption): TI18nString[] => { const rowsCopy = [...rows]; if (shuffleOption === "all") { shuffle(rowsCopy); } else if (shuffleOption === "exceptLast") { shuffleExceptLast(rowsCopy); } return rowsCopy; };This change would make the main function more concise and easier to read.
Line range hint
36-59: LGTM:getShuffledChoicesIdsfunction updated correctly.The modifications to the
getShuffledChoicesIdsfunction correctly implement the new shuffling logic using theTShuffleOptiontype. The function maintains proper handling of the "other" option and correctly implements both "all" and "exceptLast" shuffling options.While reviewing this function, I noticed a minor typo in the
calculateElementIdxfunction (which is not part of the changes in this PR). The parametercurrentQustionIdxshould becurrentQuestionIdx. Consider fixing this in a future PR or as a separate commit.packages/surveys/src/components/questions/MatrixQuestion.tsx (1)
46-51: Approve with suggestions: Row shuffling implementation using useMemo.The useMemo hook correctly implements the row shuffling functionality based on the
shuffleOption. However, there are a couple of points to consider:
The dependency array includes
question.rows[question.rows.length - 1].id, which might cause unnecessary re-renders if the last row's ID changes frequently. Consider using a more stable dependency, such as a unique identifier for the entire question or the shuffleOption itself.The eslint-disable comment suggests there might be a linting issue with the dependencies. It's worth reviewing if all necessary dependencies are included without over-specifying.
Consider refactoring the dependency array to:
}, [question.shuffleOption, question.id]);This change assumes that
question.idis a stable identifier for the question and its rows. If you need to react to changes in the rows themselves, consider adding a separaterowVersionorlastUpdatedfield to the question object that you can use in the dependency array.apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx (2)
88-104: LGTM: shuffleOptionsTypes object well-structured.The shuffleOptionsTypes object is clear and aligns with the PR objective. However, consider the necessity of the 'show' property, as it's always set to true.
If the 'show' property isn't used elsewhere in the codebase, consider simplifying the object structure:
const shuffleOptionsTypes = { none: { id: "none", label: "Keep current order", }, all: { id: "all", label: "Randomize all", }, exceptLast: { id: "exceptLast", label: "Randomize all except last option", }, };
239-263: LGTM: Shuffle options Select component implemented correctly.The Select component for shuffle options is well-implemented and functions as expected. However, consider the placement of this component within the form.
The shuffle options Select is currently placed at the end of the form, which might not be immediately visible to users. Consider moving it to a more prominent position, such as near the top of the form or just below the question headline. This could improve user experience by making the shuffle option more discoverable.
For example, you could place it after the question headline:
<QuestionFormInput id="headline" value={question.headline} label={"Question*"} // ... other props ... /> <div className="mt-3"> <Label htmlFor="shuffleOption">Row Order</Label> <Select defaultValue={question.shuffleOption} value={question.shuffleOption} onValueChange={(e: TShuffleOption) => { updateQuestion(questionIdx, { shuffleOption: e }); }}> {/* ... Select content ... */} </Select> </div> {/* ... rest of the form ... */}This placement would make the shuffle option more visible and accessible to users.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (4)
- apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx (3 hunks)
- packages/surveys/src/components/questions/MatrixQuestion.tsx (2 hunks)
- packages/surveys/src/lib/utils.ts (2 hunks)
- packages/types/surveys/types.ts (1 hunks)
🔇 Additional comments (8)
packages/surveys/src/lib/utils.ts (2)
2-3: LGTM: New type imports added.The addition of
TI18nStringandTShuffleOptiontypes is appropriate for the changes made in this file. These types will help improve type safety and code clarity in the modified functions.
Line range hint
1-59: Overall changes look good and align with PR objectives.The modifications introduced in this PR successfully implement the functionality to randomize row order in matrix questions. The changes include:
- New type definitions (
TI18nStringandTShuffleOption) for improved type safety.- A new
getShuffledRowsfunction to handle row shuffling.- Updates to the existing
getShuffledChoicesIdsfunction to use the newTShuffleOptiontype and implement the new shuffling logic.These changes are well-implemented, maintain the existing code structure, and add the new functionality without breaking existing behavior. The use of more specific types also improves the overall type safety of the code.
To ensure that these changes don't introduce any unintended side effects, please run the following verification script:
This script will help ensure that the new shuffling logic is used consistently across the codebase and that there are no remaining instances of the old string-based
shuffleOptionusage.packages/surveys/src/components/questions/MatrixQuestion.tsx (2)
8-8: LGTM: New import for row shuffling functionality.The import of
getShuffledRowsfrom "@/lib/utils" is correctly placed and aligns with the PR objective of randomizing row order in matrix questions.
Line range hint
1-214: Summary: Successful implementation of row shuffling in MatrixQuestion component.The changes effectively implement the row shuffling functionality for matrix questions, aligning with the PR objective. The implementation is minimal and focused, only modifying the necessary parts of the component. The rest of the component's logic, including rendering and event handling, remains unchanged, which helps maintain the existing functionality.
A few points to consider for future improvements:
- Ensure that the shuffling behavior is thoroughly tested, especially with different
shuffleOptionvalues and row configurations.- Consider adding documentation or comments explaining the shuffling behavior and its impact on the component's rendering.
- If not already done, update any relevant unit or integration tests to cover the new shuffling functionality.
To ensure the changes don't introduce any regressions, please run the following verification script:
This script checks for the correct export of
getShuffledRows, the definition ofshuffleOptionin types, and any remaining TODOs related to shuffling.apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx (3)
6-6: LGTM: Import statement updated correctly.The addition of
TShuffleOptionto the import statement is appropriate for implementing the new shuffling feature.
10-16: LGTM: Select component imports added.The addition of Select component imports is appropriate for implementing the new shuffle options UI element.
Line range hint
1-263: Overall: Good implementation of row randomization feature.The changes successfully implement the new feature for randomizing row order in matrix questions. The code is well-structured and aligns with the PR objectives. A few minor suggestions were made:
- Consider simplifying the
shuffleOptionsTypesobject if the 'show' property isn't used elsewhere.- Evaluate the placement of the shuffle options Select component for better user experience.
These suggestions aside, the implementation is solid and ready for integration.
packages/types/surveys/types.ts (1)
575-575: Ensure validation logic includesshuffleOptionVerify that the validation functions properly handle the new
shuffleOptionproperty to ensure it conforms to expected values and that any associated behaviors are correctly implemented.Run the following script to check if
shuffleOptionis included in validation logic:
packages/types/surveys/types.ts
Outdated
| type: z.literal(TSurveyQuestionTypeEnum.Matrix), | ||
| rows: z.array(ZI18nString), | ||
| columns: z.array(ZI18nString), | ||
| shuffleOption: ZShuffleOption.optional(), |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Consider setting a default value for shuffleOption
Since shuffleOption is optional, providing a default value such as "none" can ensure consistent behavior when the property is not specified.
Apply this diff to set a default value:
-shuffleOption: ZShuffleOption.optional(),
+shuffleOption: ZShuffleOption.optional().default("none"),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| shuffleOption: ZShuffleOption.optional(), | |
| shuffleOption: ZShuffleOption.optional().default("none"), |
Dhruwang
left a comment
There was a problem hiding this comment.
Hey @SaiSawant1 , thanks for the PR. Good work 🙌🏻🚀
Found out few smaller issues while testing
- Shuffle option select collides with Add column button
- If a shuffle option is selected, changes to row does not get reflected.(I tried adding a new row)
Screen-Recording-2024-10-01-at-4.29.36.PM.mp4
- When I select "Randomise all except last", it should not change last option
Screen-Recording-2024-10-01-at-4.30.12.PM.mp4
Rest looks good 😊✅
| const isMediaAvailable = question.imageUrl || question.videoUrl; | ||
| useTtc(question.id, ttc, setTtc, startTime, setStartTime, question.id === currentQuestionId); | ||
|
|
||
| question.rows = useMemo(() => { |
There was a problem hiding this comment.
Kindly avoid mutating this object directly. Use a constant instead 😊
| </Button> | ||
| </div> | ||
| <div className="flex flex-1 items-center justify-end gap-2"> | ||
| <Select |
There was a problem hiding this comment.
Since we have the same functionality for multi select and ranking, can you create a reusable component for Shuffle option select in packages/ui/ShuffleOptionSelect 😊
|
Hey @SaiSawant1 , whats the status here ? 😊 |
The preview matrix question was not being updated when questions was being updated.
almost done. |
packages/ui/components/ShuffleOptionSelect which is imported in MatrixQuestionForm, MultipleChoiceQuestionForm, RankingQuestionForm
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (10)
packages/ui/components/ShuffleOptionSelect/index.tsx (1)
37-68: LGTM: Component implementation is well-structured. Consider adding error handling.The
ShuffleOptionSelectcomponent is well-implemented, following React best practices. The use of destructured props and efficient rendering ofSelectItemcomponents are commendable.A minor suggestion for improvement:
Consider adding error handling or a fallback UI for cases where
shuffleOptionsTypesmight be empty or undefined. This could prevent potential runtime errors and improve the component's robustness.Example:
{Object.values(shuffleOptionsTypes).length > 0 ? ( Object.values(shuffleOptionsTypes).map( // ... existing code ) ) : ( <SelectItem value="none" disabled>No options available</SelectItem> )}This change ensures that the component always renders something, even if there are no shuffle options available.
packages/surveys/src/lib/utils.ts (4)
21-35: LGTM: New functiongetRandomRowIdximplemented correctly.The function successfully implements the shuffling logic for both "all" and "exceptLast" options, aligning with the PR objective. It correctly uses the existing
shufflefunction and handles the "exceptLast" case properly.Consider adding input validation for the
nparameter to ensure it's a positive integer:if (n <= 0 || !Number.isInteger(n)) { throw new Error("n must be a positive integer"); }
37-49: LGTM: New functiongetShuffledRowsimplemented correctly.The function successfully implements the shuffling logic for both "all" and "exceptLast" options, aligning with the PR objective. It correctly uses the existing
shufflefunction and handles the "exceptLast" case properly.Consider refactoring to reuse the logic from
getRandomRowIdx:export const getShuffledRows = (rows: TI18nString[], shuffleOption: TShuffleOption): TI18nString[] => { const shuffledIndices = getRandomRowIdx(rows.length, shuffleOption); return shuffledIndices.map(index => rows[index]); };This refactoring would reduce code duplication and improve maintainability.
51-54: LGTM: FunctiongetShuffledChoicesIdsupdated correctly.The function has been successfully updated to use the new
TShuffleOptiontype and implement the shuffling logic for both "all" and "exceptLast" options. It maintains the existing functionality for handling the "other" option.For consistency with
getShuffledRows, consider using the spread operator when creatingshuffledChoices:const shuffledChoices = otherOption ? choices.filter((choice) => choice.id !== "other") : [...choices];This change would make the code more consistent across similar functions.
Remaining usages of the old shuffle method detected.
The new shuffling functions (
getRandomRowIdx,getShuffledRows, andgetShuffledChoicesIds) are correctly integrated and used in the codebase. However, the oldshufflemethod is still being utilized withinutils.ts. Please refactor the code to replace all instances of the oldshufflefunction with the new shuffling functions to maintain consistency and improve code maintainability.🔗 Analysis chain
Line range hint
1-124: Overall implementation looks good, with room for minor improvements.The changes successfully implement the row randomization feature for matrix questions, aligning with the PR objective. The new functions and modifications to existing ones are consistent with the codebase's style and structure.
Consider grouping related functions together for better code organization. For example, you could place
getRandomRowIdx,getShuffledRows, andgetShuffledChoicesIdsin a section dedicated to shuffling operations. This would improve the file's readability and make it easier to maintain related functionality.To ensure that the changes don't introduce any unintended side effects, please run the following verification script:
This script will help identify any places where the new functions are used and ensure that the old shuffling method with a string option is no longer in use.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that the new shuffling functions are used correctly throughout the codebase. # Test: Search for usages of the new and modified functions echo "Searching for usages of getRandomRowIdx:" rg --type typescript 'getRandomRowIdx' echo "Searching for usages of getShuffledRows:" rg --type typescript 'getShuffledRows' echo "Searching for usages of getShuffledChoicesIds:" rg --type typescript 'getShuffledChoicesIds' # Test: Check for any remaining usages of the old shuffling method echo "Checking for any remaining usages of the old shuffling method:" rg --type typescript 'shuffle.*option.*string'Length of output: 731
Script:
#!/bin/bash # Description: Verify that the new shuffling functions are used correctly throughout the codebase. # Test: Search for usages of the new and modified functions within .ts and .tsx files echo "Searching for usages of getRandomRowIdx:" rg --glob '*.ts' --glob '*.tsx' 'getRandomRowIdx' echo "Searching for usages of getShuffledRows:" rg --glob '*.ts' --glob '*.tsx' 'getShuffledRows' echo "Searching for usages of getShuffledChoicesIds:" rg --glob '*.ts' --glob '*.tsx' 'getShuffledChoicesIds' # Test: Check for any remaining usages of the old shuffling method without relying on file type echo "Checking for any remaining usages of the old shuffling method:" rg 'shuffle\([^)]*\)' --glob '*.ts' --glob '*.tsx'Length of output: 2196
apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RankingQuestionForm.tsx (2)
219-224: LGTM: ShuffleOptionSelect implementation looks goodThe addition of the ShuffleOptionSelect component successfully implements the randomization feature for ranking questions, aligning with the PR objectives. The reuse of existing shuffle option types maintains consistency across the application.
Consider adding a comment explaining the purpose of the ShuffleOptionSelect component for better code readability.
You could add a comment above the ShuffleOptionSelect component to explain its purpose:
+ {/* ShuffleOptionSelect allows users to choose how to randomize the order of ranking options */} <ShuffleOptionSelect shuffleOptionsTypes={shuffleOptionsTypes} updateQuestion={updateQuestion} shuffleOption={question.shuffleOption} questionIdx={questionIdx} />
Line range hint
1-230: Consider extracting shuffleOptionsTypes for better reusabilityThe overall implementation of the RankingQuestionForm component is well-structured and successfully integrates the new randomization feature while preserving existing functionality. However, to improve code organization and reusability, consider extracting the shuffleOptionsTypes object to a separate file or a higher-level component.
You could create a new file, e.g.,
shuffleOptions.ts, and move the shuffleOptionsTypes definition there:// shuffleOptions.ts export const shuffleOptionsTypes = { none: { id: "none", label: "Keep current order", show: true, }, all: { id: "all", label: "Randomize all", show: true, // You might want to make this dynamic based on the context }, };Then, import and use it in the RankingQuestionForm component:
import { shuffleOptionsTypes } from './shuffleOptions'; // ... rest of the component code // Use shuffleOptionsTypes directly in the ShuffleOptionSelect component <ShuffleOptionSelect shuffleOptionsTypes={shuffleOptionsTypes} // ... other props />This change would make the shuffle options more reusable across different components and easier to maintain.
apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MultipleChoiceQuestionForm.tsx (1)
Line range hint
54-69: LGTM: Enhanced shuffle options with improved flexibility.The updates to the
shuffleOptionsTypesobject provide more flexibility in randomization options, which aligns well with the PR objectives. The condition for showing the "all" option based on the absence of an "other" choice is a good safeguard against potential conflicts.Consider extracting the
shuffleOptionsTypesobject to a separate constant or configuration file to improve code organization and maintainability, especially if these options might be reused elsewhere in the application.packages/types/surveys/types.ts (2)
576-576: LGTM! Consider adding a comment for clarity.The addition of the
shuffleOptionproperty toZSurveyMatrixQuestionis well-implemented and aligns with the PR objective. It uses the existingZShuffleOptiontype and provides a sensible default value, maintaining backwards compatibility.Consider adding a brief comment explaining the purpose of the
shuffleOptionproperty, e.g.:// Determines how the rows of the matrix question should be shuffled (if at all) shuffleOption: ZShuffleOption.optional().default("none"),
Line range hint
1-1024: Overall, the change is well-implemented and consistent. Consider standardizing shuffle options across question types.The addition of the
shuffleOptiontoZSurveyMatrixQuestionis isolated and doesn't negatively impact the rest of the file. It's consistent with similar implementations inZSurveyMultipleChoiceQuestionandZSurveyRankingQuestion.For improved consistency and maintainability, consider creating a reusable interface or type for questions that support shuffling. This could be applied to
ZSurveyMatrixQuestion,ZSurveyMultipleChoiceQuestion, andZSurveyRankingQuestion. For example:interface IShuffleable { shuffleOption: z.ZodOptional<typeof ZShuffleOption>; } // Then use it in the question types: export const ZSurveyMatrixQuestion = ZSurveyQuestionBase.extend({ // ... other properties }).extend<IShuffleable>({ shuffleOption: ZShuffleOption.optional().default("none"), });This approach would make it easier to manage and extend shuffle functionality across different question types in the future.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (7)
- apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx (3 hunks)
- apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MultipleChoiceQuestionForm.tsx (2 hunks)
- apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RankingQuestionForm.tsx (2 hunks)
- packages/surveys/src/components/questions/MatrixQuestion.tsx (3 hunks)
- packages/surveys/src/lib/utils.ts (2 hunks)
- packages/types/surveys/types.ts (1 hunks)
- packages/ui/components/ShuffleOptionSelect/index.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx
- packages/surveys/src/components/questions/MatrixQuestion.tsx
🔇 Additional comments (9)
packages/ui/components/ShuffleOptionSelect/index.tsx (3)
1-7: LGTM: Imports are appropriate and well-structured.The imports are correctly specified, importing necessary types from the surveys package and UI components from the local Select component. This demonstrates good code organization and type safety practices.
27-35: LGTM:ShuffleOptionSelectInterfaceis well-defined.The interface is clear, properly typed, and uses appropriate TypeScript features. The use of
Partial<...>in theupdateQuestionfunction signature allows for flexible updates, which is a good practice.
1-68: Summary: Component aligns well with PR objectives, with minor suggestions for improvement.The
ShuffleOptionSelectcomponent effectively implements the functionality to allow users to randomize the order of rows in matrix questions, as outlined in the PR objectives. It's designed to be flexible, supporting different question types (matrix, multiple-choice, ranking) through its interface.The implementation is generally well-structured and follows React and TypeScript best practices. The suggested improvements (simplifying type definitions and adding error handling) are minor and aimed at enhancing maintainability and robustness.
This component contributes significantly to addressing issue #3156 by providing a user interface for selecting shuffle options, which will help reduce bias in survey responses.
packages/surveys/src/lib/utils.ts (1)
2-3: LGTM: New type imports added.The addition of
TI18nStringandTShuffleOptiontypes aligns with the PR objective of implementing row randomization for matrix questions.apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RankingQuestionForm.tsx (2)
10-10: LGTM: Import changes align with new ShuffleOptionSelect componentThe changes in imports are consistent with the introduction of the ShuffleOptionSelect component and the removal of the Select component. This refactoring aligns well with the PR objectives of implementing row randomization for matrix questions.
Also applies to: 14-14
Line range hint
1-230: Overall: Good implementation of randomization featureThe changes in this file successfully implement the randomization feature for ranking questions, meeting the PR objectives. The code maintains good structure, preserves existing functionality, and integrates well with the current component design. The suggested minor improvements in code organization and documentation will further enhance maintainability and readability.
Great job on this implementation!
apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MultipleChoiceQuestionForm.tsx (3)
22-22: LGTM: Import of ShuffleOptionSelect aligns with PR objectives.The introduction of the
ShuffleOptionSelectcomponent and the removal of the genericSelectcomponent align well with the PR's objective of randomizing row order in matrix questions. This change suggests a more specialized and tailored approach to handling shuffle options in the survey editor.
Line range hint
1-354: Overall LGTM: Randomization feature successfully implemented.The changes in this file successfully implement the randomization feature for matrix questions, addressing the objectives outlined in the PR and the linked issue #3156. The introduction of the
ShuffleOptionSelectcomponent and the updates to the shuffle options provide the necessary functionality to randomize row order in matrix questions.Key points:
- The new
ShuffleOptionSelectcomponent is well-integrated into the existingMultipleChoiceQuestionForm.- The
shuffleOptionsTypesobject provides flexible options for randomization, including safeguards for the "other" choice.- The implementation aligns with the goal of reducing bias in survey responses.
The code changes appear to be correct and do not introduce any apparent issues or conflicts with the existing functionality. However, thorough testing of the new randomization feature in various scenarios is recommended to ensure it behaves as expected across different question configurations.
287-292: LGTM: ShuffleOptionSelect implementation looks correct.The implementation of the
ShuffleOptionSelectcomponent appears to be correct, with all necessary props being passed. This change provides more specific control over the shuffle options UI and behavior, which aligns with the PR objectives.To ensure the component is working as expected, please verify the following:
- The shuffle options are displayed correctly in the UI.
- Selecting different shuffle options updates the question state correctly.
- The "all" option is hidden when an "other" choice is present.
You can use the following script to check for the component's usage and prop passing:
✅ Verification successful
Verified: ShuffleOptionSelect usage is correct.
The
ShuffleOptionSelectcomponent is properly imported and utilized inMultipleChoiceQuestionForm.tsx, with theshuffleOptionprop correctly passed asquestion.shuffleOption. No issues were found in the implementation.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify ShuffleOptionSelect usage and prop passing # Check for ShuffleOptionSelect usage echo "Checking ShuffleOptionSelect usage:" rg --type typescript 'ShuffleOptionSelect' apps/web/app # Check for shuffleOption prop in MultipleChoiceQuestionForm echo "Checking shuffleOption prop:" rg --type typescript 'shuffleOption={question\.shuffleOption}' apps/web/appLength of output: 453
Script:
#!/bin/bash # Description: Verify ShuffleOptionSelect usage and prop passing # Check for ShuffleOptionSelect usage echo "Checking ShuffleOptionSelect usage:" rg --type ts 'ShuffleOptionSelect' apps/web/app # Check for shuffleOption prop in MultipleChoiceQuestionForm echo "Checking shuffleOption prop:" rg --type ts 'shuffleOption=\{question\.shuffleOption\}' apps/web/appLength of output: 1909
| type ShuffleOptionsTypes = { | ||
| none?: { | ||
| id: string; | ||
| label: string; | ||
| show: boolean; | ||
| }; | ||
| all?: { | ||
| id: string; | ||
| label: string; | ||
| show: boolean; | ||
| }; | ||
| exceptLast?: { | ||
| id: string; | ||
| label: string; | ||
| show: boolean; | ||
| }; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Consider simplifying the ShuffleOptionsTypes type.
The current structure of ShuffleOptionsTypes is verbose and repetitive. Consider simplifying it to reduce duplication and improve maintainability.
Here's a suggested refactoring:
type ShuffleOptionType = {
id: string;
label: string;
show: boolean;
};
type ShuffleOptionsTypes = {
none?: ShuffleOptionType;
all?: ShuffleOptionType;
exceptLast?: ShuffleOptionType;
};This approach reduces repetition and makes it easier to add new shuffle options in the future.
|
Hey @Dhruwang , I made all the changes you requested and also created the packages/ui/components/ShuffleOptionsSelect component. However, I don't know if I followed the correct coding practices while creating the component. I tried to look at other components but couldn't get any systematic approach to follow please suggest any changes to follow a particular pattern. random_matrix.mp4 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (2)
packages/ui/components/ShuffleOptionSelect/index.tsx (2)
21-29: Well-defined interface with a minor improvement suggestion.The
ShuffleOptionSelectInterfaceis comprehensive and includes all necessary properties for the component. However, to improve readability and maintainability, consider extracting theupdateQuestionfunction type into a separate type definition.Consider refactoring the
updateQuestionfunction type:type UpdateQuestionFunction = ( questionIdx: number, updatedAttributes: Partial<TSurveyMatrixQuestion | TSurveyMultipleChoiceQuestion | TSurveyRankingQuestion> ) => void; interface ShuffleOptionSelectInterface { shuffleOption: TShuffleOption | undefined; updateQuestion: UpdateQuestionFunction; questionIdx: number; shuffleOptionsTypes: ShuffleOptionsTypes; }This change would make the interface more readable and easier to maintain.
31-62: Well-implemented component with a minor suggestion.The
ShuffleOptionSelectcomponent is well-structured and follows React best practices. It effectively uses theSelectcomponent from the UI library and correctly implements the shuffle option selection functionality.Consider adding a default placeholder text for better user experience:
- <SelectValue placeholder="Select ordering" /> + <SelectValue placeholder={shuffleOption ? undefined : "Select ordering"} />This change ensures that the placeholder is only shown when no option is selected, improving the component's usability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- packages/ui/components/ShuffleOptionSelect/index.tsx (1 hunks)
🔇 Additional comments (3)
packages/ui/components/ShuffleOptionSelect/index.tsx (3)
1-7: Imports look good!The necessary types and UI components are imported correctly. There are no unused imports, which is good for maintaining a clean codebase.
9-19: Well-structured type definitions!The
ShuffleOptionTypeandShuffleOptionsTypesare clearly defined and provide a good structure for the shuffle options. The implementation follows the previous refactoring suggestion, which has resulted in a more maintainable and flexible type structure.
1-62: Overall, excellent implementation of the ShuffleOptionSelect component!This new component aligns well with the PR objectives of allowing users to randomize the order of rows in matrix questions. The implementation is flexible enough to handle different types of questions (matrix, multiple-choice, ranking) as required.
Key strengths:
- Well-structured type definitions and interface
- Clean and efficient component implementation
- Proper use of UI library components for consistency
The minor suggestions provided in the review comments will further enhance the component's maintainability and user experience. Great job on addressing the feature request outlined in issue #3156!
…randomize-row-order-matrix-questions
Dhruwang
left a comment
There was a problem hiding this comment.
Thank you for the changes, looks good 😊🚀✅
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (5)
packages/ui/components/ShuffleOptionSelect/index.tsx (1)
31-62: Well-implemented component with a minor suggestionThe
ShuffleOptionSelectcomponent is well-structured and follows React best practices. The use of theSelectcomponent from your UI library promotes consistency, and the conditional rendering ofSelectItemcomponents is a good approach.One minor suggestion for improved type safety:
onValueChange={(e: TShuffleOption) => { updateQuestion(questionIdx, { shuffleOption: e }); }}Consider using a more specific type for
einstead ofTShuffleOption. SinceshuffleOptionsTypesdefines the possible values, you could use:onValueChange={(e: keyof ShuffleOptionsTypes) => { updateQuestion(questionIdx, { shuffleOption: e }); }}This ensures that only valid shuffle options can be selected.
packages/surveys/src/lib/utils.ts (2)
21-35: LGTM: New function implements shuffling logic correctly.The
getShuffledRowIndicesfunction correctly implements the shuffling logic based on the provided option, handling both "all" and "exceptLast" cases as required. This aligns well with the PR objectives for randomizing row order in matrix questions.Consider adding a check for edge cases where
nis 0 or negative to ensure robust behavior:export const getShuffledRowIndices = (n: number, shuffleOption: TShuffleOption): number[] => { + if (n <= 0) return []; // Rest of the function remains the same };
37-40: LGTM: Function updated to use new shuffling logic.The
getShuffledChoicesIdsfunction has been successfully updated to use theTShuffleOptiontype and implement the new shuffling logic. The changes are consistent with the PR objectives and maintain the existing functionality for handling the "other" option.Consider refactoring to reuse logic from
getShuffledRowIndicesfor consistency and maintainability:export const getShuffledChoicesIds = ( choices: TSurveyQuestionChoice[], shuffleOption: TShuffleOption ): string[] => { const otherOption = choices.find((choice) => choice.id === "other"); const choicesWithoutOther = otherOption ? choices.filter((choice) => choice.id !== "other") : choices; const shuffledIndices = getShuffledRowIndices(choicesWithoutOther.length, shuffleOption); const shuffledChoices = shuffledIndices.map(index => choicesWithoutOther[index]); if (otherOption) shuffledChoices.push(otherOption); return shuffledChoices.map((choice) => choice.id); };This refactoring would centralize the shuffling logic in
getShuffledRowIndices, making the code more DRY and easier to maintain.apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx (2)
82-98: LGTM with suggestions: shuffleOptionsTypes objectThe shuffleOptionsTypes object is well-structured and covers the main scenarios for shuffling options. However, consider the following suggestions:
- If the 'show' property is always true, you might want to remove it to simplify the object structure.
- If this object is or will be used in multiple components, consider moving it to a separate constants file for better maintainability.
Example of simplified structure:
const shuffleOptionsTypes = { none: { id: "none", label: "Keep current order" }, all: { id: "all", label: "Randomize all" }, exceptLast: { id: "exceptLast", label: "Randomize all except last option" }, };
233-240: LGTM with suggestion: ShuffleOptionSelect implementationThe ShuffleOptionSelect component is correctly implemented and placed within the form structure. All necessary props are passed, which aligns with the PR objectives to allow users to randomize the order of rows in matrix questions.
Suggestion: Consider adding a brief comment above this section to explain its purpose and how it relates to the matrix question functionality. This will help other developers understand the context quickly.
Example:
{/* Shuffle options for randomizing matrix question rows */} <div className="mt-3 flex flex-1 items-center justify-end gap-2"> <ShuffleOptionSelect // ... (rest of the code) /> </div>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (4)
- apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx (3 hunks)
- packages/surveys/src/components/questions/MatrixQuestion.tsx (3 hunks)
- packages/surveys/src/lib/utils.ts (2 hunks)
- packages/ui/components/ShuffleOptionSelect/index.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/surveys/src/components/questions/MatrixQuestion.tsx
🔇 Additional comments (7)
packages/ui/components/ShuffleOptionSelect/index.tsx (3)
1-7: Imports look good!The necessary types and UI components are imported correctly. There are no unused imports, which is a good practice.
1-62: Overall, a well-implemented component with minor improvement opportunitiesThe
ShuffleOptionSelectcomponent is well-structured and achieves its purpose of allowing users to select shuffle options for survey questions. It follows React best practices and makes good use of the existing UI components.To further enhance the code:
- Consider simplifying the interface definitions as suggested earlier.
- Improve type safety in the
onValueChangehandler.- Ensure that the
shuffleOptionsTypesprop is consistently provided with the expected structure.These minor improvements will make the component more robust and easier to maintain in the future.
9-29: 🛠️ Refactor suggestionWell-structured interfaces, but consider simplification
The interfaces are well-defined and provide clear type information. However, as suggested in a previous review, consider simplifying the
ShuffleOptionsTypesinterface to reduce repetition:type ShuffleOptionType = { id: string; label: string; show: boolean; }; type ShuffleOptionsTypes = { [key in 'none' | 'all' | 'exceptLast']?: ShuffleOptionType; };This approach would make it easier to add or modify shuffle options in the future while maintaining type safety.
packages/surveys/src/lib/utils.ts (2)
2-2: LGTM: Import statement updated correctly.The addition of
TShuffleOptionto the import statement is consistent with its usage in the new and modified functions. This change aligns well with the PR objectives of implementing row order randomization for matrix questions.
Line range hint
1-40: Overall: Implementation successfully adds row randomization functionality.The changes in this file successfully implement the functionality for randomizing row order in matrix questions, aligning well with the PR objectives. The code quality is good, with clear and logical implementations of the shuffling algorithms.
A few minor suggestions have been made for improvement:
- Adding edge case handling for the
getShuffledRowIndicesfunction.- Considering a refactor of
getShuffledChoicesIdsto reuse logic fromgetShuffledRowIndices.These suggestions aim to enhance the robustness and maintainability of the code. Great job on implementing this feature!
apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx (2)
10-10: LGTM: Import statement for ShuffleOptionSelectThe import statement for the ShuffleOptionSelect component is correctly placed and follows proper naming conventions. Good job on using a shared UI component for better code reusability.
Line range hint
1-243: Summary: Successfully implemented row randomization for matrix questionsGreat job on implementing the feature to randomize row order in matrix questions! The changes align well with the PR objectives and enhance the survey design capabilities as requested in issue #3156. The code is well-structured, follows React best practices, and integrates smoothly with the existing MatrixQuestionForm component.
A few minor suggestions have been made to further improve code quality and maintainability. Overall, this implementation effectively addresses the need for reducing bias in survey responses by allowing row randomization in matrix questions.
Next steps:
- Address the minor suggestions if you agree with them.
- Ensure that you've signed the Contributor License Agreement (CLA) as mentioned in the PR checklist.
- Double-check that all tests pass and the code builds without warnings.
Once these steps are completed, this PR should be ready for final review and merging. Great work on enhancing the survey functionality!
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- packages/surveys/src/components/questions/RankingQuestion.tsx (2 hunks)
🔇 Additional comments (1)
packages/surveys/src/components/questions/RankingQuestion.tsx (1)
8-8: ImportinggetShuffledChoicesIdsThe addition of
getShuffledChoicesIdsto the import statement from@/lib/utilsis correct and allows for the new shuffling functionality.
| question.choices.filter((c) => !value.includes(c.id)) | ||
| ); | ||
| const [unsortedItems, setUnsortedItems] = useState<TSurveyQuestionChoice[]>(() => { | ||
| if (!question.shuffleOption || question.shuffleOption === "none" || sortedItems.length > 1) { |
There was a problem hiding this comment.
Verify the condition for shuffling
The condition sortedItems.length > 1 might unintentionally prevent shuffling after more than one item is sorted. If the intent is to stop shuffling once the user has started sorting, consider changing the condition to sortedItems.length > 0.
| return shuffledChoiceIds | ||
| .map((choiceId) => question.choices.find((choice) => choice.id === choiceId)) | ||
| .filter((choice) => choice !== undefined); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Optimize choice lookup to improve performance
Using find within a map can lead to inefficient lookups, especially with larger datasets. To enhance performance, consider creating a lookup map for question.choices.
Apply this diff to optimize the choice mapping:
+ const choicesMap = useMemo(() => {
+ const map = new Map<string, TSurveyQuestionChoice>();
+ question.choices.forEach((choice) => {
+ map.set(choice.id, choice);
+ });
+ return map;
+ }, [question.choices]);
return shuffledChoiceIds
- .map((choiceId) => question.choices.find((choice) => choice.id === choiceId))
+ .map((choiceId) => choicesMap.get(choiceId))
.filter((choice): choice is TSurveyQuestionChoice => choice !== undefined);Committable suggestion was skipped due to low confidence.
|
Awarding SaiSawant1: 150 points 🕹️ Well done! Check out your new contribution on oss.gg/SaiSawant1 |
What does this PR do?
Allows user to randomize row ordering in matrix question.
Fixes #3156

How should this be tested?
Checklist
Required
pnpm buildconsole.logsgit pull origin mainAppreciated
Summary by CodeRabbit
New Features
ShuffleOptionSelectcomponent for streamlined selection of shuffle methods.Bug Fixes
Documentation