Skip to content

Commit

Permalink
HTML audio video support (#1179)
Browse files Browse the repository at this point in the history
## What's Changed?

- `HtmlRunner` now looks for media in `editor.project.videos` and
`editor.project.audio` as well as `editor.project.image_list`
- Upgraded React Testing Library to reduce test warnings
  • Loading branch information
loiswells97 authored Jan 30, 2025
1 parent d554021 commit 784e86a
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Ability to write to files in `python` (#1146)
- Support for the `outputPanels` attribute in the `PyodideRunner` (#1157)
- Downloading project instructions (#1160)
- Support for audio and video files in HTML projects (#1179)

### Changed

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"@react-three/test-renderer": "8.2.1",
"@svgr/webpack": "5.5.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.2.0",
"@testing-library/react": "14.3.1",
"@testing-library/user-event": "^12.1.10",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
Expand Down
34 changes: 20 additions & 14 deletions src/components/Editor/Runners/HtmlRunner/HtmlRunner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ import { MOBILE_MEDIA_QUERY } from "../../../../utils/mediaQueryBreakpoints";
function HtmlRunner() {
const project = useSelector((state) => state.editor.project);
const projectCode = project.components;
const projectImages = project.image_list;
const projectMedia = [
...(project.image_list || []),
...(project.audio || []),
...(project.videos || []),
];

const firstPanelIndex = 0;
const focussedFileIndex = useSelector(
Expand Down Expand Up @@ -114,9 +118,9 @@ function HtmlRunner() {
const cssProjectImgs = (projectFile) => {
var updatedProjectFile = { ...projectFile };
if (projectFile.extension === "css") {
projectImages.forEach((image) => {
const find = new RegExp(`['"]${image.filename}['"]`, "g"); // prevent substring matches
const replace = `"${image.url}"`;
projectMedia.forEach((media_file) => {
const find = new RegExp(`['"]${media_file.filename}['"]`, "g"); // prevent substring matches
const replace = `"${media_file.url}"`;
updatedProjectFile.content = updatedProjectFile.content.replaceAll(
find,
replace,
Expand Down Expand Up @@ -264,27 +268,29 @@ function HtmlRunner() {

const replaceSrcNodes = (
indexPage,
projectImages,
projectMedia,
projectCode,
attr = "src",
) => {
const srcNodes = indexPage.querySelectorAll(`[${attr}]`);

srcNodes.forEach((srcNode) => {
const projectImage = projectImages.find(
const projectMediaFile = projectMedia.find(
(component) => component.filename === srcNode.attrs[attr],
);
const projectFile = projectCode.find(
const projectTextFile = projectCode.find(
(file) => `${file.name}.${file.extension}` === srcNode.attrs[attr],
);

let src = "";
if (!!projectImage) {
src = projectImage.url;
} else if (!!projectFile) {
if (!!projectMediaFile) {
src = projectMediaFile.url;
} else if (!!projectTextFile) {
src = getBlobURL(
projectFile.content,
mimeTypes.lookup(`${projectFile.name}.${projectFile.extension}`),
projectTextFile.content,
mimeTypes.lookup(
`${projectTextFile.name}.${projectTextFile.extension}`,
),
);
} else if (matchingRegexes(allowedExternalLinks, srcNode.attrs[attr])) {
src = srcNode.attrs[attr];
Expand Down Expand Up @@ -351,8 +357,8 @@ function HtmlRunner() {
body.insertAdjacentHTML("afterbegin", disableLocalStorageScript);

replaceHrefNodes(indexPage, projectCode);
replaceSrcNodes(indexPage, projectImages, projectCode);
replaceSrcNodes(indexPage, projectImages, projectCode, "data-src");
replaceSrcNodes(indexPage, projectMedia, projectCode);
replaceSrcNodes(indexPage, projectMedia, projectCode, "data-src");

body.appendChild(parse(`<meta filename="${previewFile}" />`));

Expand Down
71 changes: 71 additions & 0 deletions src/components/Editor/Runners/HtmlRunner/HtmlRunner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,77 @@ describe("When an allowed external link is rendered", () => {
});
});

describe("When media is rendered", () => {
const mediaHTML =
'<head></head><body><img src="image.jpeg" /><video src="video.mp4" /><audio src="audio.mp3" /></body>';
let generatedHtml;
beforeEach(() => {
const middlewares = [];
const mockStore = configureStore(middlewares);
const initialState = {
editor: {
project: {
components: [
{ name: "index", extension: "html", content: mediaHTML },
],
image_list: [
{
filename: "image.jpeg",
url: "https://example.com/image.jpeg",
},
],
videos: [
{
filename: "video.mp4",
url: "https://example.com/video.mp4",
},
],
audio: [
{
filename: "audio.mp3",
url: "https://example.com/audio.mp3",
},
],
},
focussedFileIndices: [0],
openFiles: [["index.html"]],
codeRunTriggered: true,
codeHasBeenRun: true,
errorModalShowing: false,
},
};
const store = mockStore(initialState);
render(
<Provider store={store}>
<MemoryRouter>
<div id="app">
<HtmlRunner />
</div>
</MemoryRouter>
</Provider>,
);
[generatedHtml] = Blob.mock.calls[0][0];
});

test("Transforms image sources", () => {
expect(generatedHtml).toContain(
'<img src="https://example.com/image.jpeg"',
);
});

test("Transforms video sources", () => {
expect(generatedHtml).toContain(
'<video src="https://example.com/video.mp4"',
);
});

test("Transforms audio sources", () => {
expect(generatedHtml).toContain(
'<audio src="https://example.com/audio.mp3"',
);
});
});

describe("When on desktop", () => {
let store;

Expand Down
50 changes: 34 additions & 16 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ __metadata:
languageName: node
linkType: hard

"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.25.7, @babel/code-frame@npm:^7.5.5":
"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.25.7, @babel/code-frame@npm:^7.5.5":
version: 7.25.7
resolution: "@babel/code-frame@npm:7.25.7"
dependencies:
Expand All @@ -123,6 +123,17 @@ __metadata:
languageName: node
linkType: hard

"@babel/code-frame@npm:^7.10.4":
version: 7.26.2
resolution: "@babel/code-frame@npm:7.26.2"
dependencies:
"@babel/helper-validator-identifier": ^7.25.9
js-tokens: ^4.0.0
picocolors: ^1.0.0
checksum: db13f5c42d54b76c1480916485e6900748bbcb0014a8aca87f50a091f70ff4e0d0a6db63cade75eb41fcc3d2b6ba0a7f89e343def4f96f00269b41b8ab8dd7b8
languageName: node
linkType: hard

"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.25.7, @babel/compat-data@npm:^7.25.8":
version: 7.25.8
resolution: "@babel/compat-data@npm:7.25.8"
Expand Down Expand Up @@ -352,6 +363,13 @@ __metadata:
languageName: node
linkType: hard

"@babel/helper-validator-identifier@npm:^7.25.9":
version: 7.25.9
resolution: "@babel/helper-validator-identifier@npm:7.25.9"
checksum: 5b85918cb1a92a7f3f508ea02699e8d2422fe17ea8e82acd445006c0ef7520fbf48e3dbcdaf7b0a1d571fc3a2715a29719e5226636cb6042e15fe6ed2a590944
languageName: node
linkType: hard

"@babel/helper-validator-option@npm:^7.25.7":
version: 7.25.7
resolution: "@babel/helper-validator-option@npm:7.25.7"
Expand Down Expand Up @@ -2753,7 +2771,7 @@ __metadata:
"@svgr/webpack": 5.5.0
"@szhsin/react-menu": ^3.2.0
"@testing-library/jest-dom": ^5.16.5
"@testing-library/react": ^13.2.0
"@testing-library/react": 14.3.1
"@testing-library/user-event": ^12.1.10
"@typescript-eslint/eslint-plugin": ^4.5.0
"@typescript-eslint/parser": ^4.5.0
Expand Down Expand Up @@ -3494,9 +3512,9 @@ __metadata:
languageName: node
linkType: hard

"@testing-library/dom@npm:^8.5.0":
version: 8.20.1
resolution: "@testing-library/dom@npm:8.20.1"
"@testing-library/dom@npm:^9.0.0":
version: 9.3.4
resolution: "@testing-library/dom@npm:9.3.4"
dependencies:
"@babel/code-frame": ^7.10.4
"@babel/runtime": ^7.12.5
Expand All @@ -3506,7 +3524,7 @@ __metadata:
dom-accessibility-api: ^0.5.9
lz-string: ^1.5.0
pretty-format: ^27.0.2
checksum: 06fc8dc67849aadb726cbbad0e7546afdf8923bd39acb64c576d706249bd7d0d05f08e08a31913fb621162e3b9c2bd0dce15964437f030f9fa4476326fdd3007
checksum: dfd6fb0d6c7b4dd716ba3c47309bc9541b4a55772cb61758b4f396b3785efe2dbc75dc63423545c039078c7ffcc5e4b8c67c2db1b6af4799580466036f70026f
languageName: node
linkType: hard

Expand All @@ -3527,17 +3545,17 @@ __metadata:
languageName: node
linkType: hard

"@testing-library/react@npm:^13.2.0":
version: 13.4.0
resolution: "@testing-library/react@npm:13.4.0"
"@testing-library/react@npm:14.3.1":
version: 14.3.1
resolution: "@testing-library/react@npm:14.3.1"
dependencies:
"@babel/runtime": ^7.12.5
"@testing-library/dom": ^8.5.0
"@testing-library/dom": ^9.0.0
"@types/react-dom": ^18.0.0
peerDependencies:
react: ^18.0.0
react-dom: ^18.0.0
checksum: 51ec548c1fdb1271089a5c63e0908f0166f2c7fcd9cacd3108ebbe0ce64cb4351812d885892020dc37608418cfb15698514856502b3cab0e5cc58d6cc1bd4a3e
checksum: b057d4c9db5a523acfc24d7bc4665a924ab8d6f252c7f51eecf7dd30f1239413e1134925fd5cc9cbdef80496af64c04e6719b2081f89fe05ba87e8c6305bcc16
languageName: node
linkType: hard

Expand Down Expand Up @@ -3936,11 +3954,11 @@ __metadata:
linkType: hard

"@types/react-dom@npm:^18.0.0":
version: 18.3.1
resolution: "@types/react-dom@npm:18.3.1"
dependencies:
"@types/react": "*"
checksum: ad28ecce3915d30dc76adc2a1373fda1745ba429cea290e16c6628df9a05fd80b6403c8e87d78b45e6c60e51df7a67add389ab62b90070fbfdc9bda8307d9953
version: 18.3.5
resolution: "@types/react-dom@npm:18.3.5"
peerDependencies:
"@types/react": ^18.0.0
checksum: 95c757684f71e761515c5a11299e5feec550c72bb52975487f360e6f0d359b26454c26eaf2ce45dd22748205aa9b2c2fe0abe7005ebcbd233a7615283ac39a7d
languageName: node
linkType: hard

Expand Down

0 comments on commit 784e86a

Please sign in to comment.