amp-up-io/qti3-item-player

Is this a Vue component or web component?

harinair opened this issue · 11 comments

I somewhere read that the QT3 Item Player is a web component library. But I could not find any examples on how to use as a web component library. I am struggling a bit trying to integrate this to our react app.

Can you integrate this into a React JS app or Angular app? Any sample code? I run into error:
Unknown custom element: <catalog-dialog> - did you register the component correctly? For recursive components, make sure to provide the "name" option.

The QTI 3 Item Player is a component, not a web component, because QTI 3 must handle MathML and LaTeX, which is in turn handled by the MathJax library. MathJax has no visibility into the shadow dom. Consequently, MathJax and proper MathML rendering across a wide variety of browsers requires that the Player is NOT a web component.

There are two ways that you can get familiar with how to use the component.

  1. Look at the README ... particularly Usage

  2. As the Usage section indicates, you can go to the qti3-item-player-controller repo. Please follow the Getting Started instructions. This repo is this exact example application in minutes. qti3-item-player-controller has a dependency on the QTI 3 Item Player component. It includes examples of how to exercise and instrument the Player.

In regard to how to use QTI 3 Item Player (which is a Vue 2.6 component) in a React app, I have not spent any time with this, but I have been advised by two people that this react-vue has worked for them. There are several online tutorials about this as well.

In regard to how to use QTI 3 Item Player in an Angular app, just wrap the Player in a CustomElement. This method has been used successfully by Angular app developers.

Thanks for the quick reply. I am importing the qti3-item-player NPM package and then I am trying to embed it to React application. I have looked at react-vue. It looks like it is mostly used for loading vue files in the same project as vue components. Maybe I am wrong. Some other React/Vue bridging/wrapping libraries use Vue3 and hence fails. I ended up using Vue to instantiate the Qti3Player and mounted it in DOM. The player is initializing and I am even getting a callback. After that, loadItemFromXml fails. Somehow the CatalogDialog component is not registered... and $ref is empty. If you can find any specific examples on loading a Vue component npm package in React, that would be helpful. I can contribute a sample React app to this repo if I am successful. There are a lot of documents on how to use React components in Vue but not the other way.

  const qtiProps = {
    ref: 'qti3player',
    ':container-class': 'containerClass',
    ':container-padding-class': 'containerPaddingClass',
    ':color-class': 'colorClass',
    'suppress-alert-messages': true,
    'suppress-invalid-response-messages': true,
    'suppress-catalog-messages': true,
  };

  useEffect(() => {
    console.log('QT3Player ****', Qti3Player);
    if (mountPoint.current) {
      const a = new Vue({
        propsData: qtiProps,
        ...Qti3Player,
      })
        .$on('notifyQti3PlayerReady', handlePlayerReady)
        .$on('notifyQti3ItemCatalogEvent', handleItemCatalogEvent)
        // '@notifyQti3ItemReady': handleItemReady,
        // '@notifyQti3SuspendAttemptCompleted': handleSuspendAttemptCompleted,
        // '@notifyQti3EndAttemptCompleted': handleEndAttemptCompleted,
        // '@notifyQti3ScoreAttemptCompleted': 'handleScoreAttemptCompleted',
        // '@notifyQti3ItemAlertEvent': 'displayItemAlertEvent',
        // '@notifyQti3ItemCatalogEvent': 'handleItemCatalogEvent',
        .$mount(mountPoint.current);
      console.log('Vue*****', a);
    }
  }, []);

It is the usual QTI example slightly modified. The problem happens in:

resetCatalogComponents() {
  this.$refs.catalogdialog.reset();
},

reset method is not found in the catalogdialog object.

Here is the XML I used:

<?xml version="1.0" encoding="UTF-8"?>
          <qti-assessment-item xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                               xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd" 
                               identifier="choice_fixed" title="Unattended Luggage" adaptive="false" time-dependent="false">
              <qti-response-declaration identifier="RESPONSE1" cardinality="single" base-type="identifier">
                  <qti-correct-response>
                      <qti-value>ChoiceA</qti-value>
                  </qti-correct-response>
              </qti-response-declaration>
              <qti-response-declaration identifier="RESPONSE2" cardinality="single" base-type="identifier">
                  <qti-correct-response>
                      <qti-value>ChoiceB</qti-value>
                  </qti-correct-response>
              </qti-response-declaration>
              <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float">
                  <qti-default-value>
                      <qti-value>0</qti-value>
                  </qti-default-value>
              </qti-outcome-declaration>
              <qti-item-body>
                  <p>Look at the text in the picture.</p>
                  <p>
                      <img src="images/sign.png" alt="NEVER LEAVE LUGGAGE UNATTENDED"/>
                  </p>
                  <qti-choice-interaction response-identifier="RESPONSE1" shuffle="true" max-choices="1">
                      <qti-prompt>What does it say?</qti-prompt>
                      <qti-simple-choice identifier="ChoiceA">You must stay with your luggage at all times.</qti-simple-choice>
                      <qti-simple-choice identifier="ChoiceB">Do not let someone else look after your luggage.</qti-simple-choice>
                      <qti-simple-choice identifier="ChoiceC">Remember your luggage when you leave.</qti-simple-choice>
                      <qti-simple-choice identifier="ChoiceD" fixed="true">None of the above.</qti-simple-choice>
                  </qti-choice-interaction>
                  <qti-choice-interaction response-identifier="RESPONSE2" shuffle="true" max-choices="1">
                      <qti-prompt>What may be the reason for posting this warning in an airport?</qti-prompt>
                      <qti-simple-choice identifier="ChoiceA">Unattended luggage can be stolen.</qti-simple-choice>
                      <qti-simple-choice identifier="ChoiceB">Unattended luggage is a security risk.</qti-simple-choice>
                      <qti-simple-choice identifier="ChoiceC">Unattended luggage will be a problem for cleaning crew.</qti-simple-choice>
                      <qti-simple-choice identifier="ChoiceD" fixed="true">None of the above.</qti-simple-choice>
                  </qti-choice-interaction>
              </qti-item-body>
              <qti-response-processing template="https://www.imsglobal.org/question/qti_v3p0/rptemplates/match_correct.xml"/>
          </qti-assessment-item>

Thanks. I would imagine .reset() is not found because catalogdialog is undefined.

I'll look at this right now and let you know if I think I've got a fix.

I tested the Player with the provided XML. All seems good (as expected). To help get to the bottom of the mysterious missing this.$refs.catalogdialog I added a console message inside of the loadItemFromXml method which will print out all Qti3Player $refs. For example, we should see something like this in the console:

image

Here we see that there are 3 $refs: catalogdialog, player, and item. Each of these refs give us a handle on the methods of the referenced component; e.g., this.$refs.catalogdialog.reset()

image

I am speculating that, for whatever reason, no $refs are getting resolved inside your react wrapper. If my speculation is correct, then perhaps Vue itself is not getting loaded and bound?

Please let me know what you find regarding the $refs.

To get this diagnostic version of the QTI 3 Item Player, simply npm install qti3-item-player@1.1.2 in your react project.

Here is my sample app using the NPM package:
https://codesandbox.io/p/sandbox/elated-rumple-fz8dkp

The direct link to the app is:
https://fz8dkp.csb.app/

It looks like there may be a problem with the way it is packaged?

If I do import Vue from 'vue', I get a warning:

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
found in
---> <Anonymous>
       <Root>

I see a lot more initialization messages but eventually the Player does not load.

When I researched about it someone suggested I do import Vue from 'vue/dist/vue'. If I do it I get another warning:

[Vue warn]: Unknown custom element: <catalog-dialog> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
(found in <Root>)

And then a error on reset not found.

Thank you for attempting to debug this.

Yes, there is a devDependency in the QTI 3 Item Player for the vue-template-compiler. These are the devDependencies from the component's package.json (note the last dependency):

  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "vue-template-compiler": "^2.6.11"
  }

However, I'm still confused by the message about the component missing.

It's definitely there, and it definitely has the proper name.

import CatalogDialog from '@/shared/components/catalog/CatalogDialog'
import QtiAssessmentItem from '@/components/qti/QtiAssessmentItem'

Vue.component('catalog-dialog', CatalogDialog)
Vue.component('qti-assessment-item', QtiAssessmentItem)

I don't know what is happening here. Did anyone use the npm package in projects other that vue? I am not using any React specific code but I cannot make it work. Is the npm package made correctly for distribution?

Ok - I have some idea. Looks like you need a Vue build process for the component to work even in non-Vue project. The project works fine in Vue projects created with now obsolete vue-cli.

If you use the new vue create which uses vite we need to change the vite config to make the library work:

  resolve: {
    alias: {
      vue: "vue/dist/vue",  // <- this line should be added
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },

For react component libraries we create, such a requirement is not there. You just need the React library to mount the component to DOM. No special changes are required on the build process. I still could not make this run on a non-vue project.

Is there a plan to make sure that the NPM qti3-item-player library work in a simple non-Vue project?