Skip to content
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

[FEATURE] Permettre de placer des custom elements dans les contenus Modulix (PIX-17186) #11700

Merged
merged 2 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions api/src/devcomp/domain/models/element/CustomElement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { assertNotNullOrUndefined } from '../../../../shared/domain/models/asserts.js';
import { Element } from './Element.js';

class CustomElement extends Element {
/**
* @param{object} params
* @param{string} params.id
* @param{string} params.tagName
* @param{string} params.props
*/
constructor({ id, tagName, props }) {
super({ id, type: 'custom' });
assertNotNullOrUndefined(tagName, 'The tagName is required for a CustomElement element');
assertNotNullOrUndefined(props, 'The props are required for a CustomElement element');
this.tagName = tagName;
this.props = props;
}
}

export { CustomElement };
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,187 @@
}
],
"grains": [
{
"id": "a39ce6eb-f3db-447f-808f-aa6a06b940c9",
"type": "lesson",
"title": "test iframe",
"components": [
{
"type": "element",
"element": {
"id": "52af6cab-07df-4962-abc5-20cf95d5eb5a",
"type": "text",
"content": "<p>Voici une iframe :</p>"
}
},
{
"type": "element",
"element": {
"id": "f00133f5-0653-425b-a25f-3c9604820529",
"type": "text",
"content": "<iframe title=\"dnd\" src=\"https://1024pix.github.io/atelier-contenus/RPE/cartes2.html\" height=\"420\"></iframe>"
}
},
{
"type": "element",
"element": {
"id": "e32c01a8-7ae1-4c6d-9a16-6487c7c504d2",
"type": "text",
"content": "<p>Elles ne sont autorisées qu'en béta.</p>"
}
}
]
},
{
"id": "0d4ef09a-ebb8-4514-a037-8fb22e540d7d",
"type": "lesson",
"title": "test web component",
"components": [
{
"type": "element",
"element": {
"id": "96e2457d-54d3-461a-97e4-69180a30bfb7",
"type": "text",
"content": "<p>Voici un web component, développé à la va-vite pour la démo :</p>"
}
},
{
"type": "element",
"element": {
"id": "cfec5a0e-2ed5-462f-8974-e5ca25faae39",
"type": "custom",
"tagName": "cartes-a-retourner",
"props": {
"cardsPerLine": 4,
"cards": [
{
"frontImageUrl": "https://i.imgur.com/ynIpt6T.png",
"backImageUrl": "https://i.imgur.com/prjj6bH.png"
},
{
"frontImageUrl": "https://i.imgur.com/Gcy9yUl.png",
"backImageUrl": "https://i.imgur.com/HVZcy58.png"
},
{
"frontImageUrl": "https://i.imgur.com/k2istl3.png",
"backImageUrl": "https://i.imgur.com/ww44Phm.png"
},
{
"frontImageUrl": "https://i.imgur.com/6MLS2tU.png",
"backImageUrl": "https://i.imgur.com/chQSy0B.png"
}
]
}
}
},
{
"type": "element",
"element": {
"id": "5e77afeb-78a8-4733-a451-fbe5fa2f360c",
"type": "text",
"content": "<p>Comme vous pouvez le voir l'expérience utilisateur n'est pas du tout la même.</p><p><em>Notamment pour les utilisateurs de lecteur d'écran</em></p>"
}
},
{
"type": "element",
"element": {
"id": "298518dc-9ad0-4709-92c0-c0a1fb1a1a2a",
"type": "text",
"content": "<p>Un avantage est que la configuration peut être précisée directement dans le contenu du module et pas côté pix-epreuves</p>"
}
}
]
},
{
"id": "0d4ef09a-ebb8-4514-a037-8fb22e540d7c",
"type": "discovery",
"title": "Voici un grain qui contient un webcomponent",
"components": [
{
"type": "element",
"element": {
"id": "5415e701-d81a-4f5e-b0e4-a0db67d83e18",
"type": "text",
"content": "<p>Voici un autre exemple de web component, le QCU Image :</p>"
}
},
{
"type": "element",
"element": {
"id": "cfec5a0e-2ed5-462f-8974-e5ca25faae38",
"type": "custom",
"tagName": "qcu-image",
"props": {
"name": "Liste d'applications",
"maxChoicesPerLine": 3,
"imageChoicesSize": "icon",
"choices": [
{
"name": "Google",
"image": {
"width": 534,
"height": 544,
"loading": "lazy",
"decoding": "async",
"src": "https://epreuves.pix.fr/_astro/Google.B1bcY5Go_1BynY8.svg"
}
},
{
"name": "LibreOffice Writer",
"image": {
"width": 205,
"height": 246,
"loading": "lazy",
"decoding": "async",
"src": "https://epreuves.pix.fr/_astro/writer.3bR8N2DK_Z1iWuJ9.webp"
}
},
{
"name": "Explorateur",
"image": {
"width": 128,
"height": 128,
"loading": "lazy",
"decoding": "async",
"src": "https://epreuves.pix.fr/_astro/windows-file-explorer.CnF8MYwI_23driA.webp"
}
},
{
"name": "Geogebra",
"image": {
"width": 640,
"height": 640,
"loading": "lazy",
"decoding": "async",
"src": "https://epreuves.pix.fr/_astro/geogebra.CZH9VYqc_19v4nj.webp"
}
}
]
}
}
},
{
"type": "element",
"element": {
"id": "73e4676d-fcc9-4ef2-b6de-b33e71922c04",
"type": "text",
"content": "<p>Pour comparer du comparable voici la même chose en version embed :</p>"
}
},
{
"type": "element",
"element": {
"id": "9c8a0dc3-4e8e-4cf9-ab37-3280931cbab7",
"type": "embed",
"isCompletionRequired": false,
"title": "QCU Image",
"url": "https://epreuves.pix.fr/fr/qcu_image/1d_iconewriter.html",
"instruction": "<p>Instruction</p>",
"height": 600
}
}
]
},
{
"id": "f312c33d-e7c9-4a69-9ba0-913957b8f7dd",
"type": "discovery",
Expand Down
11 changes: 11 additions & 0 deletions api/src/devcomp/infrastructure/factories/module-factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { BlockText } from '../../domain/models/block/BlockText.js';
import { ComponentElement } from '../../domain/models/component/ComponentElement.js';
import { ComponentStepper } from '../../domain/models/component/ComponentStepper.js';
import { Step } from '../../domain/models/component/Step.js';
import { CustomElement } from '../../domain/models/element/CustomElement.js';
import { Download } from '../../domain/models/element/Download.js';
import { Embed } from '../../domain/models/element/Embed.js';
import { Expand } from '../../domain/models/element/Expand.js';
Expand Down Expand Up @@ -89,6 +90,8 @@ export class ModuleFactory {

static #buildElement(element) {
switch (element.type) {
case 'custom':
return ModuleFactory.#buildCustom(element);
case 'download':
return ModuleFactory.#buildDownload(element);
case 'embed':
Expand Down Expand Up @@ -120,6 +123,14 @@ export class ModuleFactory {
}
}

static #buildCustom(element) {
return new CustomElement({
id: element.id,
tagName: element.tagName,
props: element.props,
});
}

static #buildDownload(element) {
return new Download({
id: element.id,
Expand Down
100 changes: 100 additions & 0 deletions api/tests/devcomp/unit/domain/models/element/CustomElement_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { CustomElement } from '../../../../../../src/devcomp/domain/models/element/CustomElement.js';
import { DomainError } from '../../../../../../src/shared/domain/errors.js';
import { catchErrSync, expect } from '../../../../../test-helper.js';

describe('Unit | Devcomp | Domain | Models | Element | CustomElement', function () {
describe('#constructor', function () {
it('should create a valid CustomElement object', function () {
// given
const attributes = {
id: '5ce0ddf1-8620-43b5-9e43-cd9b2ffaca17',
tagName: 'qcu-image',
props: {
name: "Liste d'applications",
maxChoicesPerLine: 3,
imageChoicesSize: 'icon',
choices: [
{
name: 'Google',
image: {
width: 534,
height: 544,
loading: 'lazy',
decoding: 'async',
src: 'https://epreuves.pix.fr/_astro/Google.B1bcY5Go_1BynY8.svg',
},
},
{
name: 'LibreOffice Writer',
image: {
width: 205,
height: 246,
loading: 'lazy',
decoding: 'async',
src: 'https://epreuves.pix.fr/_astro/writer.3bR8N2DK_Z1iWuJ9.webp',
},
},
{
name: 'Explorateur',
image: {
width: 128,
height: 128,
loading: 'lazy',
decoding: 'async',
src: 'https://epreuves.pix.fr/_astro/windows-file-explorer.CnF8MYwI_23driA.webp',
},
},
{
name: 'Geogebra',
image: {
width: 640,
height: 640,
loading: 'lazy',
decoding: 'async',
src: 'https://epreuves.pix.fr/_astro/geogebra.CZH9VYqc_19v4nj.webp',
},
},
],
},
};

// when
const result = new CustomElement(attributes);

// then
expect(result.id).to.equal(attributes.id);
expect(result.tagName).to.equal(attributes.tagName);
expect(result.props).to.deep.equal(attributes.props);
expect(result.type).to.equal('custom');
});
});

describe('A CustomElement without a tagName', function () {
it('should throw an error', function () {
const attributes = {
id: '5ce0ddf1-8620-43b5-9e43-cd9b2ffaca17',
};
// when
const error = catchErrSync(() => new CustomElement(attributes))();

// then
expect(error).to.be.instanceOf(DomainError);
expect(error.message).to.equal('The tagName is required for a CustomElement element');
});
});

describe('A CustomElement without props', function () {
it('should throw an error', function () {
const attributes = {
id: '5ce0ddf1-8620-43b5-9e43-cd9b2ffaca17',
tagName: 'qcu-image',
};
// when
const error = catchErrSync(() => new CustomElement(attributes))();

// then
expect(error).to.be.instanceOf(DomainError);
expect(error.message).to.equal('The props are required for a CustomElement element');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Joi from 'joi';

import { uuidSchema } from '../utils.js';

const customElementSchema = Joi.object({
id: uuidSchema,
type: Joi.string().valid('custom').required(),
tagName: Joi.string().required(),
props: Joi.object().required(),
}).required();

export { customElementSchema };
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Joi from 'joi';

import { customElementSchema } from './element/custom-element-schema.js';
import { downloadElementSchema } from './element/download-schema.js';
import { embedElementSchema } from './element/embed-schema.js';
import { expandElementSchema } from './element/expand-schema.js';
Expand Down Expand Up @@ -29,6 +30,7 @@ const moduleDetailsSchema = Joi.object({

const elementSchema = Joi.alternatives().conditional('.type', {
switch: [
{ is: 'custom', then: customElementSchema },
{ is: 'download', then: downloadElementSchema },
{ is: 'embed', then: embedElementSchema },
{ is: 'expand', then: expandElementSchema },
Expand All @@ -45,6 +47,7 @@ const elementSchema = Joi.alternatives().conditional('.type', {

const stepperElementSchema = Joi.alternatives().conditional('.type', {
switch: [
{ is: 'custom', then: customElementSchema },
{ is: 'download', then: downloadElementSchema },
{ is: 'expand', then: expandElementSchema },
{ is: 'image', then: imageElementSchema },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Fonctionnalité: Accessibilité de Modulix
Quand je vais au grain suivant
Quand je vais au grain suivant
Quand je vais au grain suivant
Quand je vais au grain suivant
Quand je vais au grain suivant
Quand je vais au grain suivant
Alors la page devrait être accessible
Quand je clique sur "Terminer"
Et que j'attends 500 ms
Expand Down
1 change: 1 addition & 0 deletions mon-pix/app/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import './deprecation-workflow';
import '@1024pix/epreuves-components';

import Application from '@ember/application';
import { init as initSentry } from '@sentry/ember';
Expand Down
Loading