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

GSLUX-707 activate style edition for drawings #161

Merged
merged 28 commits into from
Nov 15, 2024
Merged

Conversation

mki-c2c
Copy link
Contributor

@mki-c2c mki-c2c commented Oct 14, 2024

No user configured icons so far (awaiting myMaps)

JIRA issue

https://jira.camptocamp.com/browse/GSLUX-707

Description

first review to validate the testing approach (for point and polygon):
edit style => check correct URL

Screenshots

(only if the final render is different)

Copy link
Contributor

GitHub Pages links: * Luxembourg-geoportail: https://geoportail-luxembourg.github.io/luxembourg-geoportail/GSLUX-707_drawStyle/

@mki-c2c mki-c2c changed the title activate style edition for drawings GSLUX-707 activate style edition for drawings Oct 15, 2024
@mki-c2c mki-c2c requested review from tkohr, AlitaBernachot and tonio and removed request for tkohr October 22, 2024 14:59
Copy link
Contributor

@tkohr tkohr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the nice feature @mki-c2c ! Code looks mostly good to me. I left some comments and ideas regarding the testing inline. Don't forget to rebase, as the circle edition has evolved. Shouldn't have an impact here though, if I see right.

Testing it, it works really well. The only thing I noticed that didn't work is the eyedropper stick (not sure if that's what you call it) to choose a color from somewhere else. But it is the same on prod.
Also noticed that you don't have to validate style changes when switching between features. They are preserved, which might be a little strange. But prod also behaves the same here. So averything is isofunctional :-)

cy.window()
.its('featureHash')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be able to wrap this into a cypress function in the commands.ts utilities, where an import of FeatureHash should be possible. Not totally sure, but this would avoid the window workaround.

Then again, there is the question if it makes sense to import logic in the tests that we want to test? An alternative would be to test against the encoded URL values instead of the decodes ones (maybe with the decoded ones in comments). Certainly much less readable, but it would test for potential regressions in FeatureHash.

Or maybe we could manage to test the style properties directly on the openlayers features without using the URL. By getting the map from the window and then the drawLayer => source => feature => style!? Not sure.

Copy link
Contributor Author

@mki-c2c mki-c2c Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have loved to import the functions in cypress, but it did not work since the package luxembourg does not exist and must be built first. The window trick seems to be common in tips I found online and we already used it for olMap here: https://github.com/Geoportail-Luxembourg/luxembourg-geoportail/blob/main/cypress/support/commands.ts#L51

I found the e2e test via the URL to be very powerful, the encoding in the URL is a good representation of the app's state

Comment on lines 39 to 57
function onSizeChange(newSize: string | number) {
feature.featureStyle.size = parseFloat(newSize as string)
feature.changed()
}

function onAngleChange(newAngle: string | number) {
feature.featureStyle.angle = (parseFloat(newAngle as string) * Math.PI) / 180
feature.changed()
}

function onWidthChange(newWidth: string | number) {
feature.featureStyle.stroke = parseFloat(newWidth as string)
feature.changed()
}

function onTransparencyChange(newTransparency: string | number) {
feature.featureStyle.opacity =
(100 - parseFloat(newTransparency as string)) / 100
feature.changed()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the input param always be number here, as we use a number input in the html template? This could make the casting and converting unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using v-model.number to enforce events as number in the common rangefinder component

data-cy="featStyleColor"
/>
</div>
</div>
</template>

<template v-slot:size>
<template v-slot:size="slotProps">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my understanding, where does the slotProps come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it' so that I can pass the max parameter for the template configuration
cf <slot name="size" :maxsize="900"></slot>

// should preform deep copy, nut then the updqte of the OL object on the canvas would not work ??
const localFeature = props.feature
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still an open question?

src/components/draw/feature-item.vue Outdated Show resolved Hide resolved
src/components/draw/feature-item.vue Outdated Show resolved Hide resolved
src/components/draw/feature-sub-content.vue Outdated Show resolved Hide resolved
cypress/e2e/draw/draw-feat-style.cy.ts Outdated Show resolved Hide resolved
.trigger('input')
cy.get('*[data-cy="featStyleLineWidth"]').type('{selectAll}5.5')
cy.get('*[data-cy="featStyleTransparency"]').type('{selectAll}22')
cy.get('button[data-cy="featureEditValidate"]').click()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you wrap all this in a describe statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have to check potential reorganisation

})
})

describe('When checking featureStyle in OL DrawnFeature object', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually e2e is meant to mimic user behavior in the browser, so put this statement "When checking featureStyle in OL DrawnFeature object" in a describe for an e2e tests is awkward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was the result of some discussion, in theory e2e would be only capable of checking rendered pixels, but this seems a bit too picky. Both checking th URL and checking the OL features' attributes seem a good compromise for meaningful tests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was talking about the text description, not the test itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ;-)
that's easier then.

package.json Outdated
@@ -15,8 +15,8 @@
"test:unit": "vitest --environment jsdom --root .",
"test:unit:ci": "vitest run --environment jsdom --coverage",
"test:e2e": "INSTRUMENT_COVERAGE=true start-server-and-test preview :4173 'cypress open --e2e'",
"test:e2e:ci": "INSTRUMENT_COVERAGE=true start-server-and-test 'VITE_USE_PROXYURL=false vite dev --port 4173' :4173 'cypress run --browser chrome --e2e'",
"test:e2e:dev": "INSTRUMENT_COVERAGE=true start-server-and-test 'vite dev --port 4173' :4173 'cypress open --browser chrome --e2e'",
"test:e2e:ci": "INSTRUMENT_COVERAGE=true start-server-and-test 'VITE_USE_PROXYURL=false vite dev --port 4173' :4173 'cypress run --browser chromium --e2e'",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change???

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok seen together: I didn't know you couldn't run the e2e tests because your are using chromium. But apparently you have found a solution. Thx

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it was not intended to be merged without notice. I wanted the change to go into CI first, with its share of surprises.
Now, cypress is configured to use the first browser among chrome and chromium it will encounter. That should be pretty robust

<img
class="symbol-style"
v-if="!!feature.featureStyle.symbolId"
:src="`${SYMBOL_ICONS_URL}/symbol/${feature.featureStyle.symbolId}`"
Copy link
Contributor

@AlitaBernachot AlitaBernachot Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why "symbol/" is not included in the SYMBOL_ICONS_URL const?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


function onSizeChange(newSize: number) {
feature.featureStyle.size = newSize
feature.changed()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure the component should be aware of the openlayers feature implementation, it would have been great to refactor and move all this in a service or maybe add new methods to our extended Feature class that already exists.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, could be cleaner to wrap all this into the DrawnFeature...

@mki-c2c mki-c2c requested a review from tkohr November 12, 2024 08:04
Cross,
Triangle,
} as const
const currentSymbolTab: Ref<tabs> = ref(tabs.configurables)
const featureColor = 'red' // feature.olFeature.get('color') // TODO: to plug when feature ok
// const featureColor = feature.featureStyle.color
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

}

function onChangeSymbol(id: number) {
alert('onChangeSymbol TODO' + id) // TODO:
backNavigation()
feature.featureStyle.symbolId = `${id}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor: do this in a class/service, maybe extend the existing one. Same for all the functions above that is changing the feature style.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's really tricky, if we put a setter on the symbolId, it will not be able to trigger the redraw of its parent, otherwise we have to clone the whole featureStyle.

Actually this listener is the place in the code where access to both symbolId and changed() is possible.

Refactoring is possible, but not sure if these penalties will be worthwhile.

Copy link
Contributor Author

@mki-c2c mki-c2c Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to write a POC to see if it's not too complex

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I converted modifying the featureStyle attributes to cloning the featureStyle object.

I am not quite happy with this change since it slowed down the app quite a lot.

Could you please test if the app is sluggiish for you too ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The app still works fine for me, I don't see any latency. Thx for the refactor, I was thinking about a function that sets any given property of the style instead of setting the entire style, but you did it this way and it works fine 👌🏽.

let prevLabel = feature.label
let prevDescription = feature.description
// keep deep copy of previous style to be able to revert style on cancel
const prevStyle: DrawnFeatureStyle = { ...feature.featureStyle }
Copy link
Contributor

@AlitaBernachot AlitaBernachot Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the logic in keeping the style or the description + label in this specific component? You handle the change in the parent component, it is very hard to find where operations and setters are made.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case you cancel the editions, label and description are restored. This is not related to the PR, but it was a functional difference with prod

Copy link
Contributor

@AlitaBernachot AlitaBernachot Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I understand the functionality, my question was: why storing previous values here in this specific feature-sub-content.vue? what is the link with "sub-content"? You could just emit the cancel event, and the parent handles the previous values itself.

Plus I see the label is updated in the treeview but not on the map.

Copy link
Contributor

@tkohr tkohr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the adaptions @mki-c2c and for the double-checking in the tests (URL+feature)!

@@ -22,7 +22,7 @@ describe('Background selector', () => {
const layers = (<AUTWindowOlMap>window).olMap
.getLayers()
.getArray()
.filter((l: any) => l.get('cyLayerType') !== 'featureLayer')
.filter((l: any) => !/feature(Edit)?Layer/.exec(l.get('cyLayerType')))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little pity, we can't target the bgLayers directly during filtering, but I guess that's how it is.

@@ -35,7 +35,10 @@ describe('Draw "Circle"', () => {
)
cy.dragVertexOnMap(200, 200, 300, 300)
cy.get('*[data-cy="featItemLength"]').should('contain.text', '693.17 km')
cy.get('*[data-cy="featItemArea"]').should('contain.text', '38235.40 km²')
// test of 2 possible measurements due to precision issues between local tests and CI
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can get rid or adapt this comment now :-) (I see you adapted it below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

})
})

// describe('Test style edition of Point feature', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you forget to uncomment or delete this describe ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops


function onSizeChange(newSize: number) {
feature.featureStyle.size = newSize
feature.changed()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, could be cleaner to wrap all this into the DrawnFeature...

currentEditCompKey.value = undefined
}

function onClickValidate() {
const currentComponent =
editComponents[currentEditCompKey.value as keyof typeof editComponents]

prevLabel = feature.label
prevDescription = feature.description
Object.assign(prevStyle, feature.featureStyle)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object assign?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, I'll put prevStyle = {...feature.featureStyle} for homogeneity
Somehow I find
Object.assign(prevStyle, feature.featureStyle)
more concise in this case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I just saw, this writing makes the linter catch a const modification, I see a whole new advantage int the spread syntax.

cool!

@@ -13,6 +13,14 @@ export default defineConfig({

// It's IMPORTANT to return the config object
// with any changed environment variables
// console.log(JSON.stringify(config.browsers))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unnecessary comments (console.log)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hard to debug

// cy.visit('/')
// describe('Draw "Point"', () => {
// cy.get('button[data-cy="drawButton"]').click()
// cy.get('button[data-cy="drawPointButton"]').click()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to keep all these comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgot to delete

.filter((l: any) => l.get('cyLayerType') !== 'featureLayer')
.filter(
(l: any) =>
// regex match
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we know this is a regex, what is the purpose of this comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it helps reading, since nothing tells, this is a regex, I did not know this obscure syntax represents a regex

Copy link
Contributor

@AlitaBernachot AlitaBernachot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please clean your code:

  • remove unnecessary comments,
  • use "===" as much as possible (unless it is a choice on purpose),
  • prefer using {..., something, ... something else } instead of Object.assign() (unless it is on purpose, in some cases I agree Object.assign will be more usefull),
  • and refacto all functions that sets the feature style.

@AlitaBernachot
Copy link
Contributor

AlitaBernachot commented Nov 12, 2024

Also about UI:
image

Label for stroke width should be on one line. Border for style buttons should be full black (it looks like only right and bottom borders are black).

@AlitaBernachot
Copy link
Contributor

One other UI difference: when modifying a line style in prod, feature is not selected (line width is not thicker) whereas in your PR, the line is thicker and updating the stroke width preview on the map when modifying is not the definite one.

@AlitaBernachot
Copy link
Contributor

One other thing: When adding a symbol and setting a size to a point, after zooming out, the symbol keeps the same size. Ok to keep it as is (same behavior in prod).

Copy link
Contributor

@AlitaBernachot AlitaBernachot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok thx a lot for the rework, I don't see any latency, maybe someone else should test it? ping @tkohr

There are some minor things regarding the UI, it could be great to fix them before merging.

@tkohr
Copy link
Contributor

tkohr commented Nov 12, 2024

Ok thx a lot for the rework, I don't see any latency, maybe someone else should test it? ping @tkohr

No latencies on my side neither :-)

- sync line direction with feature value
- refactor line styles into mini-component
- sync button state with line style
- fix unselect behavior when clicking on map away from features
- move setRadius from composable useEdit to service to prevent creation of multiple listeners (useEdit, must remain singleton)
- fix selection, deselection of features between edit modes
@mki-c2c mki-c2c merged commit 3345a92 into main Nov 15, 2024
4 checks passed
@mki-c2c
Copy link
Contributor Author

mki-c2c commented Nov 15, 2024

fixed the UI issues and some more (related to selection deselection)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants