Skip to content

Commit b006d5d

Browse files
authored
Merge pull request #8 from kammeows/Branch_Kamakshi
resolves all merge conflicts
2 parents 093dccc + 990712a commit b006d5d

File tree

25 files changed

+1478
-1726
lines changed

25 files changed

+1478
-1726
lines changed

.babelrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"presets": [
33
"@babel/preset-react",
4-
"@babel/preset-env"
4+
"@babel/preset-env",
5+
"@babel/preset-typescript"
56
],
67
"env": {
78
"production": {

.eslintrc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,12 @@
8585
},
8686
"overrides": [
8787
{
88-
"files": ["*.stories.jsx"],
88+
"files": ["*.ts", "*.tsx"],
89+
"parser": "@typescript-eslint/parser",
90+
"plugins": ["@typescript-eslint"]
91+
},
92+
{
93+
"files": ["*.stories.@(js|jsx|ts|tsx)"],
8994
"rules": {
9095
"import/no-extraneous-dependencies": "off"
9196
}

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ terraform/.terraform/
2121
storybook-static
2222
duplicates.json
2323

24-
coverage
24+
coverage
25+
26+
*.tsbuildinfo

.storybook/main.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @type { import('@storybook/react-webpack5').StorybookConfig } */
22
const config = {
3-
stories: ['../client/**/*.stories.(jsx|mdx)'],
3+
stories: ['../client/**/*.stories.(jsx|mdx|tsx)'],
44
addons: [
55
'@storybook/addon-links',
66
'@storybook/addon-essentials',
@@ -18,19 +18,19 @@ const config = {
1818
// https://storybook.js.org/docs/react/builders/webpack
1919
// this modifies the existing image rule to exclude .svg files
2020
// since we want to handle those files with @svgr/webpack
21-
const imageRule = config.module.rules.find(rule => rule.test.test('.svg'))
22-
imageRule.exclude = /\.svg$/
21+
const imageRule = config.module.rules.find((rule) =>
22+
rule.test.test('.svg')
23+
);
24+
imageRule.exclude = /\.svg$/;
2325

2426
// configure .svg files to be loaded with @svgr/webpack
2527
config.module.rules.push({
2628
test: /\.svg$/,
2729
use: ['@svgr/webpack']
28-
})
30+
});
2931

30-
return config
31-
},
32+
return config;
33+
}
3234
};
3335

3436
export default config;
35-
36-

client/common/icons.jsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import styled from 'styled-components';
4-
import { prop } from '../theme';
54
import SortArrowUp from '../images/sort-arrow-up.svg';
65
import SortArrowDown from '../images/sort-arrow-down.svg';
76
import Github from '../images/github.svg';
@@ -35,20 +34,20 @@ import Copy from '../images/copy.svg';
3534
function withLabel(SvgComponent) {
3635
const StyledIcon = styled(SvgComponent)`
3736
&&& {
38-
color: ${prop('Icon.default')};
37+
color: ${(props) => props.Icon?.default};
3938
& g,
4039
& path,
4140
& polygon {
4241
opacity: 1;
43-
fill: ${prop('Icon.default')};
42+
fill: ${(props) => props.Icon?.default};
4443
}
4544
&:hover {
46-
color: ${prop('Icon.hover')};
45+
color: ${(props) => props.Icon?.hover};
4746
& g,
4847
& path,
4948
& polygon {
5049
opacity: 1;
51-
fill: ${prop('Icon.hover')};
50+
fill: ${(props) => props.Icon?.hover};
5251
}
5352
}
5453
}

client/images/cross.svg

Lines changed: 1 addition & 1 deletion
Loading
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { CrossIcon } from '../../../common/icons';
4+
5+
/**
6+
* Banner displays a dismissible announcement bar with a link and a close icon.
7+
* It's typically used to highlight opportunities, but use and design can be flexible.
8+
*
9+
* This component is **presentational only** — visibility logic (open/close state) should be
10+
* controlled by the parent via the `onClose` handler.
11+
*
12+
* @param {Object} props
13+
* @param {function} props.onClose - Function called when the user clicks the close (✕) button
14+
* @returns {JSX.Element} The banner component with a call-to-action link and a close button
15+
*
16+
* @example
17+
* const [showBanner, setShowBanner] = useState(true);
18+
*
19+
* {showBanner && (
20+
* <Banner onClose={() => setShowBanner(false)} />
21+
* )}
22+
*/
23+
24+
const Banner = ({ onClose }) => {
25+
// URL can be updated depending on the opportunity or announcement.
26+
const bannerURL = 'https://openprocessing.org/curation/89576';
27+
const bannerCopy = (
28+
<>
29+
We’re accepting p5.js sketches for a special curation exploring mental
30+
health and the newest features in p5.js 2.0!{' '}
31+
<span style={{ fontWeight: 600 }}>Submit by July 20!</span>
32+
</>
33+
);
34+
35+
return (
36+
<div className="banner">
37+
<a href={bannerURL}>{bannerCopy}</a>
38+
<button className="banner-close-button" onClick={onClose}>
39+
<CrossIcon Icon={{ default: '#000', hover: '#333' }} />
40+
</button>
41+
</div>
42+
);
43+
};
44+
45+
Banner.propTypes = {
46+
onClose: PropTypes.func.isRequired
47+
};
48+
49+
export default Banner;

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import IconButton from '../../../../common/IconButton';
7676
import contextAwareHinter from '../contextAwareHinter';
7777
import showRenameDialog from '../showRenameDialog';
7878
import { handleRename } from '../rename-variable';
79+
import { jumpToDefinition } from '../jump-to-definition';
7980

8081
emmet(CodeMirror);
8182

@@ -158,6 +159,16 @@ class Editor extends React.Component {
158159

159160
delete this._cm.options.lint.options.errors;
160161

162+
this._cm.getWrapperElement().addEventListener('click', (e) => {
163+
const isMac = /Mac/.test(navigator.platform);
164+
const isCtrlClick = isMac ? e.metaKey : e.ctrlKey;
165+
166+
if (isCtrlClick) {
167+
const pos = this._cm.coordsChar({ left: e.clientX, top: e.clientY });
168+
jumpToDefinition.call(this, pos);
169+
}
170+
});
171+
161172
const replaceCommand =
162173
metaKey === 'Ctrl' ? `${metaKey}-H` : `${metaKey}-Option-F`;
163174
this._cm.setOption('extraKeys', {
@@ -173,6 +184,9 @@ class Editor extends React.Component {
173184
},
174185
Enter: 'emmetInsertLineBreak',
175186
Esc: 'emmetResetAbbreviation',
187+
[`Shift-${metaKey}-E`]: (cm) => {
188+
cm.getInputField().blur();
189+
},
176190
F2: (cm) => this.renameVariable(cm),
177191
[`Shift-Tab`]: false,
178192
[`${metaKey}-Enter`]: () => null,
@@ -591,7 +605,7 @@ class Editor extends React.Component {
591605
<section className={editorSectionClass}>
592606
<div className="editor__header">
593607
<button
594-
aria-label={this.props.t('Editor.OpenSketchARIA')}
608+
aria-label={this.props.t('Editor.CloseSketchARIA')}
595609
className="sidebar__contract"
596610
onClick={() => {
597611
this.props.collapseSidebar();
@@ -601,7 +615,7 @@ class Editor extends React.Component {
601615
<LeftArrowIcon focusable="false" aria-hidden="true" />
602616
</button>
603617
<button
604-
aria-label={this.props.t('Editor.CloseSketchARIA')}
618+
aria-label={this.props.t('Editor.OpenSketchARIA')}
605619
className="sidebar__expand"
606620
onClick={this.props.expandSidebar}
607621
>

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ exports[`Nav renders dashboard version for mobile 1`] = `
335335
>
336336
<test-file-stub
337337
aria-hidden="true"
338-
classname="icons__StyledIcon-sc-xmer15-0 dStXqm"
338+
classname="icons__StyledIcon-sc-xmer15-0 kjSZIe"
339339
focusable="false"
340340
/>
341341
</button>
@@ -351,7 +351,7 @@ exports[`Nav renders dashboard version for mobile 1`] = `
351351
>
352352
<test-file-stub
353353
aria-hidden="true"
354-
classname="icons__StyledIcon-sc-xmer15-0 dStXqm"
354+
classname="icons__StyledIcon-sc-xmer15-0 kjSZIe"
355355
focusable="false"
356356
/>
357357
</button>
@@ -930,7 +930,7 @@ exports[`Nav renders editor version for mobile 1`] = `
930930
>
931931
<test-file-stub
932932
aria-hidden="true"
933-
classname="icons__StyledIcon-sc-xmer15-0 dStXqm"
933+
classname="icons__StyledIcon-sc-xmer15-0 kjSZIe"
934934
focusable="false"
935935
/>
936936
</button>
@@ -946,7 +946,7 @@ exports[`Nav renders editor version for mobile 1`] = `
946946
>
947947
<test-file-stub
948948
aria-hidden="true"
949-
classname="icons__StyledIcon-sc-xmer15-0 dStXqm"
949+
classname="icons__StyledIcon-sc-xmer15-0 kjSZIe"
950950
focusable="false"
951951
/>
952952
</button>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* eslint-disable */
2+
import parseCodeVariables from './parseCodeVariables';
3+
import { getAST, getContext } from './rename-variable';
4+
const traverse = require('@babel/traverse').default;
5+
6+
export function jumpToDefinition(pos) {
7+
const cm = this._cm;
8+
const token = cm.getTokenAt(pos);
9+
const tokenType = token.type;
10+
11+
// Only act if it's a variable
12+
if (!tokenType || !tokenType.includes('variable')) return;
13+
14+
const varName = token.string;
15+
16+
// Get full AST and variable scopes
17+
const ast = getAST(cm);
18+
const varScopeMap = parseCodeVariables(cm).varScopeMap || {};
19+
const context = getContext(cm, ast, pos, varScopeMap);
20+
21+
if (!context || !varScopeMap[context] || !varScopeMap[context].has(varName)) {
22+
console.warn(`Variable "${varName}" not found in context "${context}".`);
23+
return;
24+
}
25+
26+
// Now find the actual definition location using Babel traverse
27+
const targetIndex = cm.indexFromPos(pos);
28+
let found = false;
29+
30+
traverse(ast, {
31+
VariableDeclarator(path) {
32+
if (found) return;
33+
34+
const { node } = path;
35+
if (
36+
node.id.type === 'Identifier' &&
37+
node.id.name === varName &&
38+
node.loc
39+
) {
40+
const defPos = cm.posFromIndex(node.start);
41+
const defContext = getContext(cm, ast, defPos, varScopeMap);
42+
43+
if (defContext === context) {
44+
found = true;
45+
cm.setCursor(defPos);
46+
cm.focus();
47+
}
48+
}
49+
},
50+
51+
FunctionDeclaration(path) {
52+
// Handle function parameters like: function foo(x) { x }
53+
if (found) return;
54+
55+
const { node } = path;
56+
if (!node.params || !node.loc) return;
57+
58+
for (const param of node.params) {
59+
if (
60+
param.type === 'Identifier' &&
61+
param.name === varName &&
62+
param.loc
63+
) {
64+
const defPos = cm.posFromIndex(param.start);
65+
const defContext = getContext(cm, ast, defPos, varScopeMap);
66+
67+
if (defContext === context) {
68+
found = true;
69+
cm.setCursor(defPos);
70+
cm.focus();
71+
}
72+
}
73+
}
74+
}
75+
});
76+
77+
if (!found) {
78+
console.warn(
79+
`Definition for "${varName}" not found in context "${context}".`
80+
);
81+
}
82+
}

0 commit comments

Comments
 (0)