Compare commits

...

5 Commits

Author SHA1 Message Date
Florian Rival
a4cd9b6069 Fix npm not installing git:// dependencies 2022-03-28 13:54:28 +02:00
Florian Rival
3ad7585f92 Add a linter rule to avoid importing prop-types
Don't show in changelog
2022-03-24 11:48:07 +01:00
AlexandreS
acb69c447d Improve events sheet search with new shortcuts and put back the "Edit variable" row in variable editor 2022-03-23 15:40:57 +01:00
Elairyx
fa928b35cb Add autocompletion for the "Simulate control" action of the Top Down movement behavior (#3767) 2022-03-21 23:15:02 +01:00
Florian Rival
67810b79ce Refactor some redundant code in Text Input tests (#3768)
Don't show in changelog
2022-03-21 23:08:54 +01:00
13 changed files with 150 additions and 150 deletions

View File

@@ -58,7 +58,7 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
});
};
it('creates the DOM element', async () => {
const setupObjectAndGetDomElementContainer = async () => {
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
loadScene(runtimeScene);
@@ -70,10 +70,6 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
const object = makeTextInputRuntimeObject(runtimeScene);
runtimeScene.addObject(object);
// Check the default size.
expect(object.getWidth()).to.be(300);
expect(object.getHeight()).to.be(30);
// Check that the DOM element was created
const gameDomElementContainer = runtimeGame
.getRenderer()
@@ -83,6 +79,20 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
'Expected getDomElementContainer to return a valid container.'
);
return { runtimeScene, gameDomElementContainer, object };
};
it('creates the DOM element', async () => {
const {
runtimeScene,
gameDomElementContainer,
object,
} = await setupObjectAndGetDomElementContainer();
// Check the default size.
expect(object.getWidth()).to.be(300);
expect(object.getHeight()).to.be(30);
expect(gameDomElementContainer.hasChildNodes()).to.be(true);
const inputElement = gameDomElementContainer.querySelector('input');
@@ -98,25 +108,11 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
});
it('destroys the DOM element when the scene is paused/resumed/stopped', async () => {
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
loadScene(runtimeScene);
// Make sure the renderer is created (to test the real DOM element creation/update)
const gameContainer = document.createElement('div');
runtimeGame.getRenderer().createStandardCanvas(gameContainer);
const object = makeTextInputRuntimeObject(runtimeScene);
runtimeScene.addObject(object);
// Check that the DOM element was created
const gameDomElementContainer = runtimeGame
.getRenderer()
.getDomElementContainer();
if (!gameDomElementContainer)
throw new Error(
'Expected getDomElementContainer to return a valid container.'
);
const {
runtimeScene,
gameDomElementContainer,
object,
} = await setupObjectAndGetDomElementContainer();
expect(gameDomElementContainer.querySelector('input')).not.to.be(null);
@@ -129,28 +125,17 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
runtimeScene.unloadScene();
expect(gameDomElementContainer.querySelector('input')).to.be(null);
// Clean up - not mandatory but to avoid overloading the testing browser.
runtimeScene.unloadScene();
});
it('changes the DOM element when the object type is updated', async () => {
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
loadScene(runtimeScene);
// Make sure the renderer is created (to test the real DOM element creation/update)
const gameContainer = document.createElement('div');
runtimeGame.getRenderer().createStandardCanvas(gameContainer);
const object = makeTextInputRuntimeObject(runtimeScene);
runtimeScene.addObject(object);
// Check that the DOM element was created
const gameDomElementContainer = runtimeGame
.getRenderer()
.getDomElementContainer();
if (!gameDomElementContainer)
throw new Error(
'Expected getDomElementContainer to return a valid container.'
);
const {
runtimeScene,
gameDomElementContainer,
object,
} = await setupObjectAndGetDomElementContainer();
expect(gameDomElementContainer.querySelector('input')).not.to.be(null);
@@ -171,25 +156,11 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
});
it('hides the DOM element when the object or layer is hidden', async () => {
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
loadScene(runtimeScene);
// Make sure the renderer is created (to test the real DOM element creation/update)
const gameContainer = document.createElement('div');
runtimeGame.getRenderer().createStandardCanvas(gameContainer);
const object = makeTextInputRuntimeObject(runtimeScene);
runtimeScene.addObject(object);
// Check that the DOM element was created
const gameDomElementContainer = runtimeGame
.getRenderer()
.getDomElementContainer();
if (!gameDomElementContainer)
throw new Error(
'Expected getDomElementContainer to return a valid container.'
);
const {
runtimeScene,
gameDomElementContainer,
object,
} = await setupObjectAndGetDomElementContainer();
const inputElement = gameDomElementContainer.querySelector('input');
if (!inputElement) throw new Error('Expected input element to be found');
@@ -224,25 +195,11 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
});
it('hides the DOM element when the object is far from the camera', async () => {
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
loadScene(runtimeScene);
// Make sure the renderer is created (to test the real DOM element creation/update)
const gameContainer = document.createElement('div');
runtimeGame.getRenderer().createStandardCanvas(gameContainer);
const object = makeTextInputRuntimeObject(runtimeScene);
runtimeScene.addObject(object);
// Check that the DOM element was created
const gameDomElementContainer = runtimeGame
.getRenderer()
.getDomElementContainer();
if (!gameDomElementContainer)
throw new Error(
'Expected getDomElementContainer to return a valid container.'
);
const {
runtimeScene,
gameDomElementContainer,
object,
} = await setupObjectAndGetDomElementContainer();
const inputElement = gameDomElementContainer.querySelector('input');
if (!inputElement) throw new Error('Expected input element to be found');

View File

@@ -105,7 +105,9 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"res/conditions/keyboard.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.AddParameter("string", _("Key"))
.AddParameter("stringWithSelector",
_("Key"),
"[\"Left\", \"Right\", \"Up\", \"Down\"]")
.MarkAsAdvanced()
.SetFunctionName("SimulateControl")
.SetIncludeFile(

View File

@@ -1214,9 +1214,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"parse5": {
@@ -8019,8 +8019,8 @@
"dev": true
},
"webidl-tools": {
"version": "git://github.com/4ian/webidl-tools.git#87247d37944d9cfdecb4f73da93289929b4077df",
"from": "git://github.com/4ian/webidl-tools.git#87247d37944d9cfdecb4f73da93289929b4077df",
"version": "github:4ian/webidl-tools#348f9c03afc9d8f278efccdd74543e265a41fd11",
"from": "github:4ian/webidl-tools#348f9c03afc9d8f278efccdd74543e265a41fd11",
"dev": true,
"requires": {
"cheerio": "^0.20.0",
@@ -8032,13 +8032,13 @@
"stream-concat": "^0.1.0",
"vinyl": "^1.1.1",
"vinyl-fs": "^2.4.2",
"webidl2": "git://github.com/markandrus/webidl2.js.git#e470735423d73fbbc20d472d9e0174592b80a463",
"webidl2": "github:markandrus/webidl2.js#e470735423d73fbbc20d472d9e0174592b80a463",
"winston": "^2.2.0"
}
},
"webidl2": {
"version": "git://github.com/markandrus/webidl2.js.git#e470735423d73fbbc20d472d9e0174592b80a463",
"from": "git://github.com/markandrus/webidl2.js.git#e470735423d73fbbc20d472d9e0174592b80a463",
"version": "github:markandrus/webidl2.js#e470735423d73fbbc20d472d9e0174592b80a463",
"from": "github:markandrus/webidl2.js#e470735423d73fbbc20d472d9e0174592b80a463",
"dev": true
},
"whatwg-encoding": {
@@ -8129,9 +8129,9 @@
"dev": true
},
"winston": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz",
"integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==",
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz",
"integrity": "sha512-TWoamHt5yYvsMarGlGEQE59SbJHqGsZV8/lwC+iCcGeAe0vUaOh+Lv6SYM17ouzC/a/LB1/hz/7sxFBtlu1l4A==",
"dev": true,
"requires": {
"async": "~1.0.0",

View File

@@ -33,7 +33,7 @@
"grunt-string-replace": "^1.3.1",
"jest": "^23.5.0",
"shelljs": "^0.8.4",
"webidl-tools": "git://github.com/4ian/webidl-tools.git#87247d37944d9cfdecb4f73da93289929b4077df"
"webidl-tools": "github:4ian/webidl-tools#348f9c03afc9d8f278efccdd74543e265a41fd11"
},
"jest": {
"testEnvironment": "node",

View File

@@ -122,6 +122,10 @@
"Trans"
],
"message": "Please import Trans from @lingui/macro"
},
{
"name": "prop-types",
"message": "Don't import anything from prop-types - use Flow/TypeScript types instead."
}
]
}

View File

@@ -18,6 +18,7 @@ import {
import RaisedButton from '../UI/RaisedButton';
import { ColumnStackLayout } from '../UI/Layout';
import {
shouldBrowsePrevious,
shouldCloseOrCancel,
shouldValidate,
} from '../UI/KeyboardShortcuts/InteractionKeys';
@@ -37,6 +38,7 @@ type Props = {|
export type SearchPanelInterface = {|
focus: () => void,
markSearchResultsDirty: () => void,
isSearchOngoing: () => boolean,
|};
const SearchPanel = (
@@ -54,19 +56,6 @@ const SearchPanel = (
) => {
const searchTextField = React.useRef<?TextField>(null);
const focusSearchField = React.useCallback((): void => {
if (searchTextField.current) searchTextField.current.focus();
}, []);
const markSearchResultsDirty = React.useCallback((): void => {
setSearchResultsDirty(true);
}, []);
React.useImperativeHandle(ref, () => ({
focus: focusSearchField,
markSearchResultsDirty,
}));
const [searchText, setSearchText] = React.useState<string>('');
const [replaceText, setReplaceText] = React.useState<string>('');
const [matchCase, setMatchCase] = React.useState<boolean>(false);
@@ -89,6 +78,27 @@ const SearchPanel = (
'search-and-replace' | 'search-in-event-sentences'
>('search-and-replace');
const isSearchOngoing = React.useCallback(
(): boolean => {
return !!searchText && !searchResultsDirty;
},
[searchText, searchResultsDirty]
);
const focusSearchField = React.useCallback((): void => {
if (searchTextField.current) searchTextField.current.focus();
}, []);
const markSearchResultsDirty = React.useCallback((): void => {
setSearchResultsDirty(true);
}, []);
React.useImperativeHandle(ref, () => ({
isSearchOngoing,
focus: focusSearchField,
markSearchResultsDirty,
}));
React.useEffect(
() => {
setSearchResultsDirty(true);
@@ -171,7 +181,9 @@ const SearchPanel = (
setSearchText(searchText);
}}
onKeyPress={event => {
if (shouldValidate(event)) {
if (shouldBrowsePrevious(event)) {
onGoToPreviousSearchResult();
} else if (shouldValidate(event)) {
if (!searchResultsDirty) {
onGoToNextSearchResult();
} else {

View File

@@ -317,6 +317,14 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
_toggleSearchPanel = () => {
this.setState(
state => {
if (
state.showSearchPanel &&
this._searchPanel &&
this._searchPanel.isSearchOngoing()
) {
this._searchPanel.focus();
return;
}
const show = !state.showSearchPanel;
if (!show) {
if (this._eventSearcher) this._eventSearcher.reset();

View File

@@ -528,7 +528,7 @@ const PropertiesEditor = ({
if (!!additionalText) {
return (
<Line alignItems="baseline">
<Line alignItems="baseline" key={`section-title-${field.name}`}>
<Text displayInlineAsSpan>{field.name}</Text>
<Spacer />
<Text
@@ -540,7 +540,7 @@ const PropertiesEditor = ({
}
return (
<Line>
<Line key={`section-title-${field.name}`}>
<Text displayInlineAsSpan>{field.name}</Text>
</Line>
);

View File

@@ -19,6 +19,13 @@ export const shouldValidate = (event: SupportedEvent) => {
return event.key === 'Enter';
};
/**
* Check if the user asked to go to previous match.
*/
export const shouldBrowsePrevious = (event: SupportedEvent) => {
return event.shiftKey && event.key === 'Enter';
};
/**
* Check if the user asked to activate something.
*/

View File

@@ -10,7 +10,7 @@ import { Line, Column } from '../UI/Grid';
import RaisedButton from '../UI/RaisedButton';
type Props = {|
onAdd: () => void,
onAdd: ?() => void,
onCopy: () => void,
hasSelection: boolean,
onPaste: () => void,
@@ -46,12 +46,14 @@ const EditVariableRow = ({
</Column>
<Column>
<RaisedButton
primary
label={<Trans>Add</Trans>}
onClick={onAdd}
icon={<Add />}
/>
{onAdd ? (
<RaisedButton
primary
label={<Trans>Add</Trans>}
onClick={onAdd}
icon={<Add />}
/>
) : null}
</Column>
</Line>
);

View File

@@ -430,30 +430,20 @@ export default class VariablesList extends React.Component<Props, State> {
return (
<Column noMargin expand useFullHeight>
{allVariables.length ? (
<React.Fragment>
<ScrollView autoHideScrollbar>
<SortableVariablesListBody
variablesContainer={this.props.variablesContainer}
onSortEnd={({ oldIndex, newIndex }) => {
this.props.variablesContainer.move(oldIndex, newIndex);
this.forceUpdate();
}}
helperClass="sortable-helper"
useDragHandle
lockToContainerEdges
>
{allVariables}
</SortableVariablesListBody>
</ScrollView>
<EditVariableRow
onAdd={this.addVariable}
onCopy={this.copySelection}
onPaste={this.paste}
onDeleteSelection={this.deleteSelection}
hasSelection={hasSelection(this.state.selectedVariables)}
hasClipboard={Clipboard.has(CLIPBOARD_KIND)}
/>
</React.Fragment>
<ScrollView autoHideScrollbar>
<SortableVariablesListBody
variablesContainer={this.props.variablesContainer}
onSortEnd={({ oldIndex, newIndex }) => {
this.props.variablesContainer.move(oldIndex, newIndex);
this.forceUpdate();
}}
helperClass="sortable-helper"
useDragHandle
lockToContainerEdges
>
{allVariables}
</SortableVariablesListBody>
</ScrollView>
) : this.props.emptyPlaceholderTitle &&
this.props.emptyPlaceholderDescription &&
this.props.helpPagePath ? (
@@ -467,6 +457,14 @@ export default class VariablesList extends React.Component<Props, State> {
/>
</Column>
) : null}
<EditVariableRow
onAdd={allVariables.length ? this.addVariable : null}
onCopy={this.copySelection}
onPaste={this.paste}
onDeleteSelection={this.deleteSelection}
hasSelection={hasSelection(this.state.selectedVariables)}
hasClipboard={Clipboard.has(CLIPBOARD_KIND)}
/>
</Column>
);
}

View File

@@ -1,3 +1,4 @@
// @flow
import * as React from 'react';
const style = {
@@ -7,8 +8,10 @@ const style = {
type Props = {
children: React.Node,
height: number | string,
alignItems?: 'center',
justifyContent?: 'center',
};
export default ({ children, height }: Props) => (
<div style={{ ...style, height }}>{children}</div>
export default ({ children, height, alignItems, justifyContent }: Props) => (
<div style={{ ...style, height, alignItems, justifyContent }}>{children}</div>
);

View File

@@ -6,6 +6,7 @@ import muiDecorator from '../ThemeDecorator';
import paperDecorator from '../PaperDecorator';
import { EmptyPlaceholder } from '../../UI/EmptyPlaceholder';
import FixedHeightFlexContainer from '../FixedHeightFlexContainer';
export default {
title: 'UI Building Blocks/EmptyPlaceholder',
@@ -13,11 +14,17 @@ export default {
decorators: [paperDecorator, muiDecorator],
};
export const Default = () => (
<EmptyPlaceholder
title="Add your first event"
description="You can use events to create cause and effect."
actionLabel="Add something"
helpPagePath="/objects/tiled_sprite"
onAdd={action('onAdd')}
/>
<FixedHeightFlexContainer
height={500}
justifyContent="center"
alignItems="center"
>
<EmptyPlaceholder
title="Add your first event"
description="You can use events to create cause and effect."
actionLabel="Add something"
helpPagePath="/objects/tiled_sprite"
onAdd={action('onAdd')}
/>
</FixedHeightFlexContainer>
);