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

[Backport 2022.02.xx] #8718 Slipping of points when depth test is active (a) (#8720) #8758

Merged
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
9 changes: 9 additions & 0 deletions web/client/components/styleeditor/Fields.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import MarkSelector from './MarkSelector';
import Band from './Band';
import IconInput from './IconInput';
import SelectInput from './SelectInput';
import MultiInput from './MultiInput';

const FormControl = localizedProps('placeholder')(FormControlRB);

Expand Down Expand Up @@ -121,6 +122,14 @@ export const fields = {
</PropertyField>
);
},
multiInput: (props) => {
return (
<PropertyField
label={props.label}>
<MultiInput {...props} />
</PropertyField>
);
},
toolbar: ({
label,
value,
Expand Down
99 changes: 99 additions & 0 deletions web/client/components/styleeditor/MultiInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright 2022, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import Select from 'react-select';
import { Glyphicon, MenuItem, DropdownButton, FormGroup, FormControl as FormControlRB } from 'react-bootstrap';
import localizedProps from '../misc/enhancers/localizedProps';
import Message from '../I18N/Message';

const ReactSelect = localizedProps(['placeholder', 'noResultsText'])(Select);
const FormControl = localizedProps('placeholder')(FormControlRB);

function MultiInput({
label,
value,
config: {
initialOptionValue,
getSelectOptions = () => [],
selectClearable = false
} = {},
onChange,
disabled,
...props
}) {

const selectOptions = getSelectOptions(props);

const dropdownButtonOptions = [
{ labelId: "styleeditor.constantValue", value: "constant" },
{ labelId: "styleeditor.attributeValue", value: "attribute" }
];
const dropdownButtonValue = value?.type === 'constant' ? 'constant' : 'attribute';

const onConstantValueChangeHandler = (event) => {
onChange({
...value,
value: event.target.value
});
};

const onSelectValueChangeHandler = (option) => {
if (option.value === initialOptionValue) {
onChange({ type: "initial" });
return;
}
onChange({ type: "attribute", name: option.value });
};

const handleDropdownButtonSelect = (newValue) => {
if (value.type !== newValue) {
onChange({
type: newValue
});
}
};

return (<div className="flex-center">
{value?.type === 'constant' && <FormGroup>
<FormControl
type="number"
disabled={disabled}
value={value?.value}
placeholder="styleeditor.placeholderInput"
onChange={onConstantValueChangeHandler}
/>
</FormGroup>}
{value?.type !== 'constant' && <div className="flex-grow-1">
<ReactSelect
disabled={disabled}
clearable={selectClearable}
options={selectOptions}
value={value?.type === 'initial' ? initialOptionValue : value?.name}
onChange={onSelectValueChangeHandler}
/>
</div>}
<DropdownButton
className="square-button-md no-border flex-center"
noCaret
pullRight
disabled={disabled}
title={<Glyphicon glyph="option-vertical" />}>
{dropdownButtonOptions.map((option) => (
<MenuItem
key={option.value}
active={dropdownButtonValue === option.value}
onClick={() => handleDropdownButtonSelect(option.value)}>
<Message msgId={option.labelId} />
</MenuItem>
))}
</DropdownButton>
</div >);
}

export default MultiInput;
118 changes: 118 additions & 0 deletions web/client/components/styleeditor/__tests__/MultiInput-test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2022, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';

import ReactDOM from 'react-dom';
import MultiInput from '../MultiInput';
import expect from 'expect';
import { Simulate, act } from 'react-dom/test-utils';

describe('MultiInput component', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});

afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});

it('should render with default', () => {
ReactDOM.render(<MultiInput />, document.getElementById("container"));
const selectNode = document.querySelector('.Select');
expect(selectNode).toBeTruthy();
});

it('should render with constant value', () => {
ReactDOM.render(<MultiInput value={{ type: 'constant', value: 123.123}} />, document.getElementById("container"));
const inputNode = document.querySelector('.form-control');
expect(inputNode.value).toBe("123.123");
});

it('should render with select value', () => {
const INITIAL_OPTION_VALUE = 'original_option_value';
const config = {
initialOptionValue: INITIAL_OPTION_VALUE,
selectOptions: [
{ label: 'Height', value: 'height' },
{ label: 'Id', value: 'id' },
{ label: 'Original value', value: INITIAL_OPTION_VALUE }
]
};
ReactDOM.render(<MultiInput value={{ type: 'attributes', name: 'height' }} config={config} />, document.getElementById("container"));
const inputNode = document.querySelector('.form-control');
expect(inputNode).toBeFalsy();
const selectNode = document.querySelector('.Select');
expect(selectNode).toBeTruthy();
});

it('should handle onChange', () => {
const callbacks = {
onChange: () => { }
};
const spy = expect.spyOn(callbacks, 'onChange');
ReactDOM.render(<MultiInput value={{ type: 'constant', value: 123.123 }} onChange={callbacks.onChange} />, document.getElementById("container"));
const inputNode = document.querySelector('.form-control');
inputNode.value = '-33';
Simulate.change(inputNode);
expect(spy).toHaveBeenCalledWith({ type: 'constant', value: '-33' });
});

it('should change height mode', () => {
const callbacks = {
onChange: () => { }
};
const spy = expect.spyOn(callbacks, 'onChange');
ReactDOM.render(<MultiInput value={{ type: 'constant', value: 123.123}} onChange={callbacks.onChange} />, document.getElementById("container"));
const buttonNode = document.querySelector('button');
Simulate.click(buttonNode);
const menuItems = document.querySelectorAll('ul.dropdown-menu li a');
expect(menuItems.length).toBe(2);
Simulate.click(menuItems[1]);
expect(spy).toHaveBeenCalledWith({ type: 'attribute' });
});

it('should render with select value', () => {
const callbacks = {
onChange: () => { }
};
const INITIAL_OPTION_VALUE = 'original_option_value';
const config = {
initialOptionValue: INITIAL_OPTION_VALUE,
getSelectOptions: () => [
{ label: 'Height', value: 'height' },
{ label: 'Id', value: 'id' },
{ label: 'Original value', value: INITIAL_OPTION_VALUE }
]
};
const spy = expect.spyOn(callbacks, 'onChange');
ReactDOM.render(
<MultiInput

value={{ type: 'attribute', name: 'height' }}
config={config} onChange={callbacks.onChange}
/>,
document.getElementById("container")
);
const selectNode = document.querySelector('.Select');
const selectInputNode = selectNode.querySelector('input');
act(() => {
Simulate.focus(selectInputNode);
Simulate.keyDown(selectInputNode, { key: 'ArrowDown', keyCode: 40 });
});
const selectMenuOptionNodes = selectNode.querySelectorAll('.Select-option');
expect(selectMenuOptionNodes.length).toBe(3);
act(() => {
Simulate.mouseDown(selectMenuOptionNodes[1]);
});
expect(spy).toHaveBeenCalledWith({ type: 'attribute', name: 'id' });
});
});
29 changes: 26 additions & 3 deletions web/client/components/styleeditor/config/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,30 @@ const vector3dStyleOptions = {
})
};

const INITIAL_OPTION_VALUE = '__height_mode_original__';
const billboard3dStyleOptions = {
msBringToFront: property.msBringToFront({
label: 'styleeditor.msBringToFront'
label: "styleeditor.msBringToFront"
}),
msHeightReference: property.msHeightReference({
label: "styleeditor.heightReferenceFromGround"
}),
msHeight: property.multiInput({
label: "styleeditor.height",
key: "msHeight",
isDisabled: (value, properties) => properties?.msHeightReference === 'clamp',
initialOptionValue: INITIAL_OPTION_VALUE,
getSelectOptions: ({ attributes }) => {
const numberAttributes = attributes
.map(({ label, attribute, type }) =>
type === "number" ? { label, value: attribute } : null
)
.filter((x) => !!x);
return [
...numberAttributes,
{ label: 'Point height', value: INITIAL_OPTION_VALUE }
];
}
})
};

Expand Down Expand Up @@ -81,7 +102,8 @@ const getBlocks = ({
strokeWidth: 1,
radius: 16,
rotate: 0,
msBringToFront: false
msBringToFront: false,
msHeightReference: 'none'
}
},
Icon: {
Expand Down Expand Up @@ -126,7 +148,8 @@ const getBlocks = ({
opacity: 1,
size: 32,
rotate: 0,
msBringToFront: false
msBringToFront: false,
msHeightReference: 'none'
}
},
Line: {
Expand Down
59 changes: 59 additions & 0 deletions web/client/components/styleeditor/config/property.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,28 @@ const property = {
},
setValue: (value) => !!value
}),
msHeightReference: ({ key = 'msHeightReference', label = 'Height reference from ground' }) => ({
type: 'toolbar',
label,
config: {
options: [{
labelId: 'styleeditor.none',
value: 'none'
}, {
labelId: 'styleeditor.relative',
value: 'relative'
}, {
labelId: 'styleeditor.clamp',
value: 'clamp'
}]
},
getValue: (value) => {
return {
[key]: value,
...(value === 'clamp' && { msHeight: undefined })
};
}
}),
shape: ({ label, key = 'wellKnownName' }) => ({
type: 'mark',
label,
Expand Down Expand Up @@ -455,6 +477,43 @@ const property = {
isDisabled,
isVisible
}),
multiInput: ({ label, key = '', initialOptionValue, getSelectOptions = () => [], isDisabled, isVisible }) => ({
type: 'multiInput',
label,
config: {
initialOptionValue,
getSelectOptions
},
setValue: (value) => {
if (value === undefined) {
return { type: 'initial' };
}
if (!isObject(value)) {
return {
type: 'constant',
value
};
}
return value;
},
getValue: (value) => {
if (value?.type === 'initial') {
return {
[key]: undefined
};
}
if (value?.type === 'constant') {
return {
[key]: value?.value ?? 0
};
}
return {
[key]: value
};
},
isDisabled,
isVisible
}),
colorRamp: ({ label, key = '', getOptions = () => [] }) => ({
type: 'colorRamp',
label,
Expand Down
5 changes: 5 additions & 0 deletions web/client/themes/default/less/style-editor.less
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,11 @@ Ported to CodeMirror by Peter Kroon
flex: 1;
}
}

.flex-grow-1 {
flex-grow: 1;
}

&.ms-symbolizer-value-invalid {
outline: 1px solid @ms-danger;
}
Expand Down
6 changes: 6 additions & 0 deletions web/client/translations/data.de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -2250,6 +2250,10 @@
"fill": "Füllfarbe",
"clampToGround": "Klemme an Masse",
"msBringToFront": "In den Vordergrund bringen",
"heightReferenceFromGround": "Höhenbezug vom boden",
"height": "Höhe",
"constantValue": "Konstanter wert",
"attributeValue": "Attributwert",
"strokeColor": "Linienfarbe",
"strokeWidth": "Linienbreite",
"radius": "Radius",
Expand All @@ -2258,6 +2262,8 @@
"3dTile": "3D",
"terrain": "Gelände",
"both": "Beide",
"relative": "Relativ",
"clamp": "Klemme",
"addIconSymbolizer": "Symbol-Symbologie hinzufügen",
"addIconRule": "Symbolregel hinzufügen",
"image": "Bild",
Expand Down
Loading