feat: add AnswerSettings and QuestionSettingsHeader components - #6059
feat: add AnswerSettings and QuestionSettingsHeader components #6059Abhishek-Punhani wants to merge 2 commits into
Conversation
…iceInteractionEditor Signed-off-by: Abhishek-Punhani <punhani.manavabhi@gmail.com>
…eaders Signed-off-by: Abhishek-Punhani <punhani.manavabhi@gmail.com>
|
👋 Hi @Abhishek-Punhani, thanks for contributing! For the review process to begin, please verify that the following is satisfied:
Also check that issue requirements are satisfied & you ran Pull requests that don't follow the guidelines will be closed. Reviewer assignment can take up to 2 weeks. |
|
@AlexVelezLl, Vue 2.7 doesn't support the built-in Teleport component, so I've added a custom component to mirror its behaviour. Let me know if you have a better implementation in mind! |
|
Oh, apologies @Abhishek-Punhani 😅, I just knew we had already used it in our ecosystem and didn't recall it was a dependency package. In KDS, we use vue2-teleport. Could you install that same version on Studio, please? I read it has some memory optimizations that'd be good to have! |
AlexVelezLl
left a comment
There was a problem hiding this comment.
Thanks @Abhishek-Punhani! I think there is a better way to do this to not remove the type selector from the DOM when we change the question type. Please let us know if there is any questions!
There was a problem hiding this comment.
Oh, given that this will only be used in the editor, could we move this component to the choice folder instead?
| settings: { | ||
| type: Array, | ||
| required: true, | ||
| validator: arr => arr.every(setting => ['shuffle', 'showAnswerCount'].includes(setting)), | ||
| }, |
There was a problem hiding this comment.
Given that this will only be rendered for choice interactions, I think it's fine to let it infer when to display each based on the questionType instead of this settings prop.
| :title="showAnswerCountInfoTitle$()" | ||
| @cancel="showAnswerCountModal = false" | ||
| > | ||
| <p :style="{ color: $themeTokens.annotation }"> |
There was a problem hiding this comment.
I think we can leave the normal text color here, instead of this annotation.
| <template #actions> | ||
| <KButton | ||
| :text="closeBtnLabel$()" | ||
| @click="showAnswerCountModal = false" | ||
| /> | ||
| </template> |
There was a problem hiding this comment.
We can also just use the cancelText prop instead of this actions slot. Usually the actions slot is used for more complex button layouts.
| :title="shuffleAnswersInfoTitle$()" | ||
| @cancel="showShuffleModal = false" | ||
| > | ||
| <p :style="{ color: $themeTokens.annotation }"> |
There was a problem hiding this comment.
Actually, the most important responsibility of this component is the type selector, so could we reference "type selector" in the name instead of "SettingsHeader"? (Similarly for class names like question-settings-header, etc)
| <template> | ||
|
|
||
| <div | ||
| v-if="mode === 'edit'" |
There was a problem hiding this comment.
We sometimes have issues because of rendering conditions on the root element of a component, and a lint rule will soon be added to avoid this, so could we move forward with this condition and leave this responsibility to the parent component instead? i.e. let the parent do <QuestionTypeSelector v-if="mode==='edit'" instead
| <div class="select-display-row"> | ||
| <KIcon | ||
| icon="language" | ||
| class="select-globe-icon" | ||
| :style="{ color: $themePalette.grey.v_700 }" | ||
| /> | ||
| <span class="select-value-text">{{ selectedOption.label }}</span> | ||
| </div> |
| @update:showAnswerCount="setShowAnswerCount" | ||
| /> | ||
| </template> | ||
| </QuestionSettingsHeader> |
There was a problem hiding this comment.
Oh, some comments: the interaction editors should not be the ones responsible for rendering this "settings header," which is actually a type selector, mainly because this will cause the DOM to remove these nodes when the component is unmounted. The type selector (and therefore, the whole header row) should be shared across all interaction editors and rendered independently of them, so that if we change the question type, the type selector component is not removed from the DOM (which would cause some accessibility issues).
So, what we can do instead is:
- Have the
InteractionSectioncomponent be the one that renders theQuestionTypeSelectorcomponent. This way, each interaction has its own type selector independent of the editor being rendered. - Let the
QuestionTypeSelectorbe the one responsible for rendering the div with the proper ID so that editor components can target it (instead of using a slot). - We can take advantage of the fact that we can only have one item being edited at a time, and because of this, there will always only be one question type selector rendered at a time, and use a constant as
idso that we don't have to keep track of any identifiers yet.
With this, the idea would be:
// QuestionTypeSelector
<div class="type-selector>
...
<div id="qti-interaction-settings" />
...// InteractionSection
<QuestionTypeSelector ... />
<component :is="descriptor.editorComponent" ... />// ChoiceInteractionEditor
<div class="choice-editor">
<Teleport to="qti-interaction-settings">
<AnswerSettings ... />
</Teleport>
....
</div>This way only the interaction editors that actually wants to add settings are the only ones that needs to teleport anything.
| const questionTypeOptions = computed(() => [ | ||
| { | ||
| value: QuestionType.SINGLE_SELECT, | ||
| label: singleSelectLabel$(), | ||
| description: singleChoiceDescription$(), | ||
| }, | ||
| { | ||
| value: QuestionType.MULTI_SELECT, | ||
| label: multiSelectLabel$(), | ||
| description: multipleSelectionDescription$(), | ||
| }, | ||
| ]); |
There was a problem hiding this comment.
Another little problem with this is that the questionType selector should render all question types for all interactions, not only the ones they are related to.
Summary
Added
QuestionSettingsHeaderandAnswerSettingscomponents to the QTI editor using portals.This PR adds:
QuestionSettingsHeader/index.vue— UI component for selecting interaction types.AnswerSettings/index.vue— UI component for interaction-specific configurations.References
Closes #6033
Reviewer guidance
AI usage
Used Antigravity for a final review and minor code/style nitpicks. I reviewed all suggested changes, kept only the relevant improvements, and verified that the implementation worked as intended after applying them.