Skip to content

Commit 11fd7c3

Browse files
committed
metadata
2 parents 57cd56a + 5b7b8d8 commit 11fd7c3

42 files changed

Lines changed: 13446 additions & 20215 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.babelrc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@
6262
}
6363
]
6464
]
65+
},
66+
"test": {
67+
"presets": [
68+
"@babel/preset-env",
69+
[
70+
"@babel/preset-react",
71+
{
72+
"runtime": "automatic"
73+
}
74+
]
75+
],
76+
"plugins": [
77+
"babel-plugin-styled-components"
78+
]
6579
}
6680
},
6781
"plugins": [

.github/CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ For first-time contributors or those who want to start with a small task, [check
4646
If you're looking for a bigger project to take on, look through the issues tagged [good medium issue](https://github.com/processing/p5.js-web-editor/labels/good%20medium%20issue). These issues are self-contained projects that may take longer to work on, but are great if you're looking to get more deeply involved in contributing!
4747

4848
### Project Board
49-
Many issues are related to each other and fall under bigger projects. To get a bigger picture, look at the [All Projects](https://github.com/processing/p5.js-web-editor/projects/4) board.
49+
Many issues are related to each other and fall under bigger projects. To get a bigger picture, look at the [All Projects](https://github.com/processing/p5.js-web-editor/projects/) board.
5050

5151
### Project Ideas
5252
If you're looking for inspiration for Google Summer of Code or a bigger project, there's a [project list](https://github.com/processing/processing/wiki/Project-List#p5js-web-editor) maintained on the Processing wiki.

README.md

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ Learn more about [our community](https://p5js.org/community/) and read our commu
1414

1515
## Getting Started
1616

17-
Make your first sketch in the [p5.js Editor](https://editor.p5js.org/)! Learn more about sketching with p5.js on the [Get Started](https://p5js.org/get-started/) and find everything you can do in the [Reference](https://p5js.org/reference/). You can also look at [examples](https://editor.p5js.org/p5/sketches) and remix them in the p5.js Editor.
17+
Make your first sketch in the [p5.js Editor](https://editor.p5js.org/)! Learn more about sketching with p5.js on the [Get Started](https://p5js.org/tutorials/get-started/) and find everything you can do in the [Reference](https://p5js.org/reference/). You can also look at [examples](https://editor.p5js.org/p5/sketches) and remix them in the p5.js Editor.
18+
19+
## Contributing
20+
21+
[Contributing to the p5.js Web Editor](https://github.com/processing/p5.js-web-editor/blob/develop/contributor_docs/README.md)
22+
23+
[p5.js Community Statement](https://editor.p5js.org/code-of-conduct)
1824

1925

2026
## Issues
@@ -24,18 +30,6 @@ If you have found a bug in the p5.js Web Editor, you can file it under the ["iss
2430
* p5.js library: [https://github.com/processing/p5.js/issues](https://github.com/processing/p5.js/issues)
2531
* p5.js website: [https://github.com/processing/p5.js-website/issues](https://github.com/processing/p5.js-website/issues)
2632

27-
28-
## References for Contributing to the p5.js Web Editor
29-
30-
[Code of Conduct](https://editor.p5js.org/code-of-conduct)
31-
32-
[Contribution Guidelines for p5.js](https://p5js.org/contributor-docs/#/)
33-
34-
[Contribution Guidelines for the p5.js Web Editor](https://github.com/processing/p5.js-web-editor/tree/develop/contributor_docs)
35-
36-
[p5.js Community Statement](https://p5js.org/community/)
37-
38-
3933
## Acknowledgements
4034

4135
Support for this project has come from [Processing Foundation](https://processingfoundation.org/), [NYU ITP](https://tisch.nyu.edu/itp), [CS4All, NYC DOE](http://cs4all.nyc/), [COSA at DU](https://liberalarts.du.edu/emergent-digital-practices/open-source-arts), [STUDIO for Creative Inquiry](https://studioforcreativeinquiry.org/), [Grant for the Web](https://www.grantfortheweb.org/), [New Media Rights](https://www.newmediarights.org/), and many others.

client/modules/IDE/components/Editor/index.jsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ class Editor extends React.Component {
168168
},
169169
Enter: 'emmetInsertLineBreak',
170170
Esc: 'emmetResetAbbreviation',
171+
[`Shift-Tab`]: false,
171172
[`${metaKey}-Enter`]: () => null,
172173
[`Shift-${metaKey}-Enter`]: () => null,
173174
[`${metaKey}-F`]: 'findPersistent',
@@ -209,10 +210,6 @@ class Editor extends React.Component {
209210
if (/^[a-z]$/i.test(e.key) && (mode === 'css' || mode === 'javascript')) {
210211
this.showHint(_cm);
211212
}
212-
if (e.key === 'Escape') {
213-
e.preventDefault();
214-
this._cm.getInputField().blur();
215-
}
216213
});
217214

218215
this._cm.getWrapperElement().style[

client/modules/IDE/components/FileNode.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ const FileNode = ({
292292
ref={fileOptionsRef}
293293
tabIndex="0"
294294
onClick={toggleFileOptions}
295+
onBlur={() => setTimeout(hideFileOptions, 200)}
295296
>
296297
<DownArrowIcon focusable="false" aria-hidden="true" />
297298
</button>

client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ exports[`Nav renders dashboard version for mobile 1`] = `
211211
-ms-flex-direction: column;
212212
flex-direction: column;
213213
gap: 0.16666666666666666rem;
214-
box-shadow: rgba(60,64,67,0.3) 0px 1px 2px 0px, rgba(60,64,67,0.15) 0px 1px 3px 1px;
214+
box-shadow: rgba(60,64,67,0.3) 0px 1px 2px 0px,rgba(60,64,67,0.15) 0px 1px 3px 1px;
215215
min-width: 10rem;
216216
border-radius: 0.16666666666666666rem;
217217
}
@@ -324,7 +324,7 @@ exports[`Nav renders dashboard version for mobile 1`] = `
324324
>
325325
<test-file-stub
326326
aria-hidden="true"
327-
classname="sc-fujyAs cSTVlM"
327+
classname="icons__StyledIcon-sc-xmer15-0 dStXqm"
328328
focusable="false"
329329
/>
330330
</button>
@@ -340,7 +340,7 @@ exports[`Nav renders dashboard version for mobile 1`] = `
340340
>
341341
<test-file-stub
342342
aria-hidden="true"
343-
classname="sc-iqAclL iOZiVo"
343+
classname="icons__StyledIcon-sc-xmer15-0 dStXqm"
344344
focusable="false"
345345
/>
346346
</button>
@@ -912,7 +912,7 @@ exports[`Nav renders editor version for mobile 1`] = `
912912
-ms-flex-direction: column;
913913
flex-direction: column;
914914
gap: 0.16666666666666666rem;
915-
box-shadow: rgba(60,64,67,0.3) 0px 1px 2px 0px, rgba(60,64,67,0.15) 0px 1px 3px 1px;
915+
box-shadow: rgba(60,64,67,0.3) 0px 1px 2px 0px,rgba(60,64,67,0.15) 0px 1px 3px 1px;
916916
min-width: 10rem;
917917
border-radius: 0.16666666666666666rem;
918918
}
@@ -1025,7 +1025,7 @@ exports[`Nav renders editor version for mobile 1`] = `
10251025
>
10261026
<test-file-stub
10271027
aria-hidden="true"
1028-
classname="sc-fujyAs cSTVlM"
1028+
classname="icons__StyledIcon-sc-xmer15-0 dStXqm"
10291029
focusable="false"
10301030
/>
10311031
</button>
@@ -1041,7 +1041,7 @@ exports[`Nav renders editor version for mobile 1`] = `
10411041
>
10421042
<test-file-stub
10431043
aria-hidden="true"
1044-
classname="sc-iqAclL iOZiVo"
1044+
classname="icons__StyledIcon-sc-xmer15-0 dStXqm"
10451045
focusable="false"
10461046
/>
10471047
</button>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// resolveUtils.unit.test.jsx
2+
3+
import resolvePathsForElementsWithAttribute from '../../../../../server/utils/resolveUtils';
4+
import { resolvePathToFile } from '../../../../../server/utils/filePath';
5+
6+
// Mock the dependencies
7+
jest.mock('../../../../../server/utils/filePath', () => ({
8+
resolvePathToFile: jest.fn()
9+
}));
10+
11+
jest.mock('../../../../../server/utils/fileUtils', () => ({
12+
MEDIA_FILE_REGEX: /\.(png|jpg|jpeg|gif|svg)$/i
13+
}));
14+
15+
describe('resolvePathsForElementsWithAttribute', () => {
16+
let mockSketchDoc;
17+
let mockFiles;
18+
19+
beforeEach(() => {
20+
jest.clearAllMocks();
21+
22+
// Create a mock DOM environment
23+
mockSketchDoc = document.implementation.createHTMLDocument();
24+
mockFiles = {
25+
'image.png': { url: 'https://example.com/image.png' },
26+
'missing.jpg': { url: null }
27+
};
28+
29+
resolvePathToFile.mockImplementation((fileName, files) => files[fileName]);
30+
});
31+
32+
it('should update the attribute when the file is resolved successfully', () => {
33+
const element = mockSketchDoc.createElement('img');
34+
element.setAttribute('src', 'image.png');
35+
mockSketchDoc.body.appendChild(element);
36+
37+
resolvePathsForElementsWithAttribute('src', mockSketchDoc, mockFiles);
38+
39+
expect(element.getAttribute('src')).toBe('https://example.com/image.png');
40+
});
41+
42+
it('should not update the attribute when the file resolution fails', () => {
43+
const element = mockSketchDoc.createElement('img');
44+
element.setAttribute('src', 'missing.jpg');
45+
mockSketchDoc.body.appendChild(element);
46+
47+
resolvePathsForElementsWithAttribute('src', mockSketchDoc, mockFiles);
48+
49+
expect(element.getAttribute('src')).toBe('missing.jpg');
50+
});
51+
52+
it('should not update the attribute when the value does not match MEDIA_FILE_REGEX', () => {
53+
const element = mockSketchDoc.createElement('img');
54+
element.setAttribute('src', 'document.pdf');
55+
mockSketchDoc.body.appendChild(element);
56+
57+
resolvePathsForElementsWithAttribute('src', mockSketchDoc, mockFiles);
58+
59+
expect(element.getAttribute('src')).toBe('document.pdf');
60+
});
61+
62+
it('should do nothing when no elements with the specified attribute are found', () => {
63+
resolvePathsForElementsWithAttribute('src', mockSketchDoc, mockFiles);
64+
65+
expect(mockSketchDoc.querySelectorAll('[src]').length).toBe(0);
66+
});
67+
});

client/modules/IDE/components/Sidebar.jsx

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useRef } from 'react';
1+
import React, { useRef, useEffect } from 'react';
22
import classNames from 'classnames';
33
import { useTranslation } from 'react-i18next';
44
import { useDispatch, useSelector } from 'react-redux';
@@ -30,21 +30,37 @@ export default function SideBar() {
3030
);
3131
const isExpanded = useSelector((state) => state.ide.sidebarIsExpanded);
3232
const canEditProject = useSelector(selectCanEditSketch);
33+
const isAuthenticated = useSelector(getAuthenticated);
3334

3435
const sidebarOptionsRef = useRef(null);
3536

36-
const isAuthenticated = useSelector(getAuthenticated);
37+
useEffect(() => {
38+
function handleClickOutside(event) {
39+
if (
40+
projectOptionsVisible &&
41+
sidebarOptionsRef.current &&
42+
!sidebarOptionsRef.current.contains(event.target)
43+
) {
44+
setTimeout(() => dispatch(closeProjectOptions()), 300);
45+
}
46+
}
3747

38-
const onBlurComponent = () => {
39-
setTimeout(() => dispatch(closeProjectOptions()), 200);
40-
};
48+
if (projectOptionsVisible) {
49+
document.addEventListener('mousedown', handleClickOutside);
50+
} else {
51+
document.removeEventListener('mousedown', handleClickOutside);
52+
}
53+
54+
return () => {
55+
document.removeEventListener('mousedown', handleClickOutside);
56+
};
57+
}, [projectOptionsVisible, dispatch]);
4158

4259
const toggleProjectOptions = (e) => {
4360
e.preventDefault();
4461
if (projectOptionsVisible) {
4562
dispatch(closeProjectOptions());
4663
} else {
47-
sidebarOptionsRef.current?.focus();
4864
dispatch(openProjectOptions());
4965
}
5066
};
@@ -65,9 +81,7 @@ export default function SideBar() {
6581
dispatch(collapseSidebar());
6682
dispatch(closeProjectOptions());
6783
}}
68-
>
69-
{' '}
70-
</button>
84+
/>
7185
)}
7286
<section className={sidebarClass}>
7387
<header
@@ -77,57 +91,54 @@ export default function SideBar() {
7791
<h3 className="sidebar__title">
7892
<span>{t('Sidebar.Title')}</span>
7993
</h3>
80-
<div className="sidebar__icons">
94+
<div className="sidebar__icons" ref={sidebarOptionsRef}>
8195
<button
8296
aria-label={t('Sidebar.ToggleARIA')}
8397
className="sidebar__add"
8498
tabIndex="0"
85-
ref={sidebarOptionsRef}
8699
onClick={toggleProjectOptions}
87-
onBlur={onBlurComponent}
88100
>
89101
<PlusIcon focusable="false" aria-hidden="true" />
90102
</button>
91-
<ul className="sidebar__project-options">
92-
<li>
93-
<button
94-
aria-label={t('Sidebar.AddFolderARIA')}
95-
onClick={() => {
96-
dispatch(newFolder(rootFile.id));
97-
setTimeout(() => dispatch(closeProjectOptions()), 0);
98-
}}
99-
onBlur={onBlurComponent}
100-
>
101-
{t('Sidebar.AddFolder')}
102-
</button>
103-
</li>
104-
<li>
105-
<button
106-
aria-label={t('Sidebar.AddFileARIA')}
107-
onClick={() => {
108-
dispatch(newFile(rootFile.id));
109-
setTimeout(() => dispatch(closeProjectOptions()), 0);
110-
}}
111-
onBlur={onBlurComponent}
112-
>
113-
{t('Sidebar.AddFile')}
114-
</button>
115-
</li>
116-
{isAuthenticated && (
103+
{projectOptionsVisible && (
104+
<ul className="sidebar__project-options">
105+
<li>
106+
<button
107+
aria-label={t('Sidebar.AddFolderARIA')}
108+
onClick={() => {
109+
dispatch(newFolder(rootFile.id));
110+
setTimeout(() => dispatch(closeProjectOptions()), 300);
111+
}}
112+
>
113+
{t('Sidebar.AddFolder')}
114+
</button>
115+
</li>
117116
<li>
118117
<button
119-
aria-label={t('Sidebar.UploadFileARIA')}
118+
aria-label={t('Sidebar.AddFileARIA')}
120119
onClick={() => {
121-
dispatch(openUploadFileModal(rootFile.id));
122-
setTimeout(() => dispatch(closeProjectOptions()), 0);
120+
dispatch(newFile(rootFile.id));
121+
setTimeout(() => dispatch(closeProjectOptions()), 300);
123122
}}
124-
onBlur={onBlurComponent}
125123
>
126-
{t('Sidebar.UploadFile')}
124+
{t('Sidebar.AddFile')}
127125
</button>
128126
</li>
129-
)}
130-
</ul>
127+
{isAuthenticated && (
128+
<li>
129+
<button
130+
aria-label={t('Sidebar.UploadFileARIA')}
131+
onClick={() => {
132+
dispatch(openUploadFileModal(rootFile.id));
133+
setTimeout(() => dispatch(closeProjectOptions()), 300);
134+
}}
135+
>
136+
{t('Sidebar.UploadFile')}
137+
</button>
138+
</li>
139+
)}
140+
</ul>
141+
)}
131142
</div>
132143
</header>
133144
<ConnectedFileNode id={rootFile.id} canEdit={canEditProject} />

client/modules/IDE/hooks/custom-hooks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ export const useModalBehavior = (hideOverlay) => {
4747
};
4848

4949
// Usage: useEffectWithComparison((props, prevProps) => { ... }, { prop1, prop2 })
50-
// This hook basically applies useEffect but keeping track of the last value of relevant props
51-
// So you can passa a 2-param function to capture new and old values and do whatever with them.
50+
// This hook basically applies useEffect but keeps track of the last value of relevant props
51+
// So you can pass a 2-param function to capture new and old values and do whatever with them.
5252
export const useEffectWithComparison = (fn, props) => {
5353
const [prevProps, update] = useState({});
5454

0 commit comments

Comments
 (0)