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

Feature 1773 layer categories #2274

Merged
merged 14 commits into from
Mar 13, 2024

Conversation

yvonnesjy
Copy link
Collaborator

@yvonnesjy yvonnesjy commented Feb 20, 2024

Support "layerCategories" as an XML config option that has "layers" nested under an array of category objects. This is so that we can group and collapse layers as the number of supported layers grows.

The "layers" config option is still supported for portals that don't use categories.

Fixes #1773

IconUtilities contains code shared by MapAsset and a new AssetCategory model (in a separate commit).
The two classes are modeled after MapAsset and MapAssets.
* @class AssetCategories
* @extends Backbone.Collection
// TODO: yvonneshi - update
// * @since x.x.x
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Current release is 2.27.1 and this is targeting end of March release. How do I derive the release number here?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Robyn fills in these x.x.x strings during release time. Safe to leave as x.x.x

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks!

src/js/models/maps/AssetCategory.js Show resolved Hide resolved
@robyngit robyngit marked this pull request as draft February 21, 2024 15:02
@robyngit robyngit linked an issue Feb 21, 2024 that may be closed by this pull request
Copy link
Collaborator

@ianguerin ianguerin left a comment

Choose a reason for hiding this comment

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

Going forward, it's helpful for me to see unit tests alongside implementation. Obviously we're getting acquainted with this codebase and new dev workflow, but I wanted to say that out loud

src/js/models/maps/assets/MapAsset.js Outdated Show resolved Hide resolved
src/js/common/IconUtilities.js Outdated Show resolved Hide resolved
src/js/collections/maps/AssetCategories.js Outdated Show resolved Hide resolved
src/js/collections/maps/AssetCategories.js Show resolved Hide resolved
src/js/models/maps/AssetCategory.js Outdated Show resolved Hide resolved
src/js/models/maps/AssetCategory.js Outdated Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems the screenshot is missing some icons?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Some layer icons 404'ed, which is out of scope for this PR. I intentionally left some of the category icons blank to show that's how it looks if portal owner doesn't specify one

src/js/views/maps/LayerCategoryItemView.js Outdated Show resolved Hide resolved
src/js/views/maps/MapView.js Outdated Show resolved Hide resolved
@robyngit
Copy link
Member

Hey @yvonnesjy looks like you made a ton of progress super quickly, nice! 🎉 I saw you said that this PR is a WIP so I set it as a "draft" for now. Please click "ready for review" and assign me as a reviewer when you feel it's ready.

Maybe it's something your working on, but I did a little testing and just wanted to make sure you're aware that the changes here do seem to break existing cesium map set ups. For example, if you switch the the theme to DRP and look at the data page (which uses the same Map model and CesiumWidgetView), the map doesn't render because of an error with geohashes:

DRP error details
Uncaught TypeError: geohashLayer.replaceGeohashes is not a function
    at n.updateGeohashCounts (Map-Search.js?v=2.27.1:302:22)
    at n.<anonymous> (Map-Search.js?v=2.27.1:177:16)
    at _ (backbone.js:370:57)
    at m (backbone.js:356:19)
    at f (backbone.js:155:16)
    at u.trigger (backbone.js:346:5)
    at n.reset (backbone.js:952:33)
    at t.success (backbone.js:1050:20)
    at c (jquery-1.9.1.min.js?v=2.27.1:3:7857)
    at Object.fireWith [as resolveWith] (jquery-1.9.1.min.js?v=2.27.1:3:8658)

I don't want to expand your work to entail anything outside of the PDG, but one requirement is implementing layer categories is that the changes don't break existing portals or data catalogs that are using cesium.

I won't go too deep into reviewing while you're still working on things, please just let me know if/when you'd like feedback.

@yvonnesjy
Copy link
Collaborator Author

yvonnesjy commented Feb 23, 2024

@robyngit Nice catch and I'm happy to report that the unit tests caught the bug too! This should be fixed now and the PR is ready for review! (I don't have access to assign reviewers)

BTW can you elaborate on "switch the theme to DRP and look at the data page"? Should be handy for future changes

@yvonnesjy yvonnesjy marked this pull request as ready for review February 23, 2024 09:05
* MapAssetConfigs are passed to a {@link MapAssets} collection.
* MapAssetConfigs are passed to a {@link MapAssets} collection. When layerCategories
* exist, this property will be ignored.
* @property {MapConfig#MapAssetConfig[]} [layerCategories] - A collection of
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@robyngit Given the new field introduced here, I imagine we need an update to docs/docs/MapConfig.html? Blame suggests these docs are autogenerated before release though- I take that it's a noop in this PR?

Copy link
Member

Choose a reason for hiding this comment

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

Yes! Thank you for thinking of documentation. :) All of the API docs are auto-generated from the JSdocs in all of the JS files. That's why it's so important to add/update JSdocs for any new or changed methods or properties. These docs are built at each release. If you'd like to test the docs your self, see this README. (But please don't commit the changes from your new doc build, it just complicates the git & PR history.)

It looks like you added the new option to the MapConfig namespace so the matching documentation will be generated at release time! 🎉

The two views are modeled after LayerListView and LayerItemView.

The styling of the views doesn't fully represent the mock yet. Specifically:
- Margin of layer items
- Padding of tool bar panel
- "Layers" title
- Font and color of icons and texts

These will be done in separate PRs.

Mock: https://www.sketch.com/s/eea6a4f3-5c59-40e6-bf7f-64edc511e4cb
1. Add layerCategories as an attribute which takes its value from the XML config.
2. Add getLayerGroups() that supports both layers and layerCategories, while layerCategories takes precedence.
3. Replace all call sites of map.get("layers") with map.getLayerGroups().
The try-catch blocks appear throughout the codebase but don't meaningfully handle the the errors. I went through the changes in this PR and removed all instances of try-catch where we simply console.log the error.
Sinon seems to be the standard framework that works with Mocha and chai. See sinonjs.org
@yvonnesjy yvonnesjy force-pushed the feature-1773-layer-categories branch 2 times, most recently from 36e3d39 to 6ed8091 Compare February 25, 2024 02:11
1. Add a default layer category icon (same icon as the layers button on the sidebar)
2. Update expand/collapse toggle to use caret instead of chevron
@mbjones
Copy link
Member

mbjones commented Feb 28, 2024

@yvonnesjy Robyn has been out, so I will add my minimally-informed 2 cents, in hope it might help you move forward.

  1. Set a theme by changing the theme path in the root index.html file
// The path to your configuration file for MetacatUI. This can be any web-accessible location.
var appConfigPath = "/js/themes/drp/config.js";
  1. Themes we maintain are either 1) in the metacatui repo itself (see https://github.com/NCEAS/metacatui/tree/main/src/js/themes), or 2) in https://github.com/NCEAS/metacatui-themes (which is where the drp theme is located). In reality themes can come from anywhere as they are just a collection of html, css, and js template files. Some sites maintain their own.

@iannesbitt can probably help with the drp testing, as he maintains that theme.

@robyngit
Copy link
Member

@yvonnesjy

Nice catch and I'm happy to report that the unit tests caught the bug too! This should be fixed now and the PR is ready for review! (I don't have access to assign reviewers)

Yay for unit tests! :) Nice work

BTW can you elaborate on "switch the theme to DRP and look at the data page"? Should be handy for future changes

Oops! Sorry for glossing over that without any instructions. DRP is one of our themes like "arctic", "knb", or "dataone", except that it is not shipped with MetacatUI; it's an external theme. External themes are stored in the metacatui-themes repository. So to test MetacatUI with the DRP theme, you need to clone the themes repo, then symlink the drp theme to the MetacatUI themes directory:

ln -s {PATH-TO-METACATUI-THEMES-REPO}/src/drp/js/themes/drp {PATH-TO-METACATUI-REPO}/src/js/themes/drp

You can set your appConfig path like Matt indicated, OR in your config/config.js file, you can simply set the "theme" to drp.

Let me know if you have any more questions about this or if I can clarify anything at all!

Testing in DRP revealed a bug that should've been caught by unit tests. Added more test coverage to files affected by refactoring.
src/js/views/maps/CesiumWidgetView.js Show resolved Hide resolved
@@ -719,101 +731,10 @@ define([
* @param {string} icon An SVG string to use for the MapAsset icon
*/
updateIcon: function (icon) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

An error here is handled in initialize:model.set("iconStatus", "error")

@@ -268,36 +268,30 @@ define(
* @returns {LayerDetailsView} Returns the rendered view
*/
renderLayerDetails: function () {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

An error here is silently caught in render()

src/js/models/connectors/Map-Search.js Outdated Show resolved Hide resolved
Copy link
Member

@robyngit robyngit left a comment

Choose a reason for hiding this comment

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

Awesome @yvonnesjy! Really appreciate how thorough you are with the tests. Only very minor comments (in line). The only other suggestion is that we should handle the situation gracefully when there are no layers and no layerCategories configured. Given that the portals will be configurable by users, this could theoretically happen. If you set both config.layers and config.layerCategories to null, you'll see the entire portal fails to render (beyond just the map). Ideally, we should see a blank Cesium globe and the portal in this case. Very excited to see this in production! 🎉

src/js/collections/maps/AssetCategories.js Show resolved Hide resolved
/** @lends AssetCategories.prototype */ {

model: AssetCategory,
modelId: attrs => {
Copy link
Member

Choose a reason for hiding this comment

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

Where is this modelId used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This one I truly forget and can't seem to reproduce an error without it. Removed and tests are still passing.

@@ -98,6 +98,8 @@ define([
}
},

modelId: attrs => attrs.cid,
Copy link
Member

Choose a reason for hiding this comment

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

Same for this modelId, where is it used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh my- here's the mental roller coaster I went through just for the giggles: The need for modelId is so tucked away until something (deep inside backbone) breaks very subtly. Multiple times now I added this fix and then removed it later cause I forgot why... until I went down the rabbit hole again... but I didn't learn my lesson to add a comment right away!

Now I've just gotten getting so upset cause I forgot the reason once again... until I realized the new tests caught it!!

That said, here's the issue, and what a better fix I've found since:

Problem: Without explicit modelId, backbone picks up the default "" as id for every map asset (unless they're set in XML, which isn't the case for PDG). This creates a problem whenever MapAssets.add/remove is called, or event listeners are involved. Specifically here, where this.get(model) references id, which is "" for every model.

Fix: After spending more time on this, I think rather than adding modelId which I keep forgetting the reasons for, a better fix would just be to remove id: "" from the default.

Copy link
Member

Choose a reason for hiding this comment

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

Oh no, what a frustrating sounding issue!! It seems like having "id" as a model attribute in MapAsset is a conflict with backbone. This attribute is intended to be used to specify the package ID for an associated dataset on a dataone repo (like the Arctic Data Center). I think we could rename this pid (what the IDs are usually called on dataone), or maybe datapackage_id. This will mean updating the PDG and DRP portal xml, but I think it's better we do this now while cesium isn't in wide use and we have control over the portal document content.

@@ -0,0 +1,74 @@
/*global define */
Copy link
Member

Choose a reason for hiding this comment

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

Thank you for moving this to its own module! 👍🏻

src/js/models/connectors/Map-Search.js Outdated Show resolved Hide resolved
*/
template: _.template(Template),

classNames: {
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be helpful to include JS docs here indicating on which elements each class is used

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

hmmm what do you mean by "elements"? as in the html tags?

Copy link
Member

Choose a reason for hiding this comment

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

More generally, what parts of the view those html tags represent or what the class is used for.

src/js/views/maps/LayerCategoryItemView.js Outdated Show resolved Hide resolved
src/js/views/maps/LayerCategoryItemView.js Outdated Show resolved Hide resolved
src/js/views/maps/LayerCategoryListView.js Show resolved Hide resolved
src/js/views/maps/CesiumWidgetView.js Show resolved Hide resolved
@yvonnesjy
Copy link
Collaborator Author

Awesome @yvonnesjy! Really appreciate how thorough you are with the tests. Only very minor comments (in line). The only other suggestion is that we should handle the situation gracefully when there are no layers and no layerCategories configured. Given that the portals will be configurable by users, this could theoretically happen. If you set both config.layers and config.layerCategories to null, you'll see the entire portal fails to render (beyond just the map). Ideally, we should see a blank Cesium globe and the portal in this case. Very excited to see this in production! 🎉

weeeee. fixed the issue by returning [] instead of null in getLayerGroups()

Logic changes:
1. Removed default "" id from MapAsset so that Backbone doesn't confuse all MapAsset's as one
2. Stop listening before adding listeners in Map-Search.js
3. In initialize()'s, check for existence of options before dereferencing
I couldn't bring myself to write JSDoc for these classnames that are supposed to be implementation details. We'll keep them in the "private" space as file constants instead.
Copy link
Member

@robyngit robyngit left a comment

Choose a reason for hiding this comment

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

🎉🎉🎉

@robyngit robyngit merged commit c376a11 into NCEAS:develop Mar 13, 2024
1 check passed
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.

Support nested data layers
4 participants