Skip to content

[18.0][ADD] web_json_widget#3231

Open
len-foss wants to merge 3 commits intoOCA:18.0from
lambdao-dev:18.0-web_json_widget-len
Open

[18.0][ADD] web_json_widget#3231
len-foss wants to merge 3 commits intoOCA:18.0from
lambdao-dev:18.0-web_json_widget-len

Conversation

@len-foss
Copy link
Copy Markdown
Contributor

Add a Json editor widget.
Supports edits, dark mode (should work on OCA's version, since it relies on setting the color_theme cookie to dark). Can even be put in a tree view, although you probably don't want to do that.

This module uses the library jsoneditor.
It is released under the Apache-2.0 license.

Screenshot_20250727_140139 Screenshot_20250727_140217

@len-foss len-foss force-pushed the 18.0-web_json_widget-len branch from 68c5758 to b13df6a Compare July 27, 2025 12:50
@ivs-cetmix
Copy link
Copy Markdown
Member

Hello @len-foss and thank you for your contribution!
Could you please create a simple web_json_widget_demo module like this one that would serve as an example and also will allow to do functional review of your PR? Just add a field to the res.partner module same way as on your screenshot.

@len-foss
Copy link
Copy Markdown
Contributor Author

@ivs-cetmix should be ready to test 👍

Copy link
Copy Markdown
Member

@ivs-cetmix ivs-cetmix left a comment

Choose a reason for hiding this comment

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

Perfect, thank you @len-foss!

Here are some glitches I encountered during testing on MacOs 15.6 (24G84) in Safari Version 18.6 (20621.3.11.11.3)

Issue 1

Steps to reproduce (on runboat)

  • In the second (text) key start typing "qwerty"

Expected behaviour

  • The entire string can be typed in without loosing the focus

Current behaviour

  • Need to click in the widget after each symbol is typed in. Eg "q" -> click, "w" -> click , ....

Issue2

Field properties drop down is rendered below the chatter buttons.

Video of both issues.

@len-foss len-foss force-pushed the 18.0-web_json_widget-len branch from 26e4630 to 8cd2914 Compare August 17, 2025 14:53
@len-foss
Copy link
Copy Markdown
Contributor Author

@ivs-cetmix
First, thank you for the testing. This is valuable feedback.

Easy point 2; I've added a CSS rule to increase the z-index. Good catch.

So, point 1. I actually broke editing trying to do a "quick patch" of a very specific problem: if the user calls the record switcher while the recordset is dirty, the component does not go through onWillUpdateProps, like it does if changes were already saved. As a result, you'd get outdated data in these cases. I'm not sure what is the proper solution, but I followed the approach of the chatter (which is to keep track of the displayed record, so that we can update if it changed). In any case, everything should be working now.

Copy link
Copy Markdown
Member

@ivs-cetmix ivs-cetmix left a comment

Choose a reason for hiding this comment

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

Functional review LGTM
@len-foss works now, thank you!
P.S. If you have LinkedIn we can promote this module to get more attention and have it merged faster 😄

};
static defaultProps = {};

setup() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your createEditor() already has a great docstring. Adding the same for setup() and detect_dark_mode() helps future developers:
/**

  • Setup lifecycle hooks and state initialization.
    • Tracks record resId and resModel
    • Binds editor creation on mount
    • Cleans up editor on unmount
      */
      setup() { ... }

/**

  • Detect if dark mode is active.
  • Currently relies on cookie color_scheme.
    */
    detect_dark_mode() { ... }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Currently, the code is pretty short, at barely a 100 lines of logic. Adding such fluff would only make it less readable, and would only serve a purpose if it was used by some automatic documentation generation tool, which aren't used within the project.

this.state.value = new_value;
this.props.record.update({[this.props.name]: new_value});
} catch (error) {
console.error("Error updating JSON value:", error);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Replace console.error with Odoo’s Notification service
this.env.services.notification.add(_t("Error updating JSON: ") + error.message, { type: "danger" });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done 👍

onChange: () => {
try {
const new_value = this.editor.get();
this.state.value = new_value;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

also update state with setter
this.state.value = { ...newValue };

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done 👍

Copy link
Copy Markdown

@antoniodavid antoniodavid left a comment

Choose a reason for hiding this comment

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

LGTM

@OCA-git-bot
Copy link
Copy Markdown
Contributor

This PR has the approved label and has been created more than 5 days ago. It should therefore be ready to merge by a maintainer (or a PSC member if the concerned addon has no declared maintainer). 🤖

@ivs-cetmix
Copy link
Copy Markdown
Member

Hi @len-foss , could you please check the code review comment and reply or apply them? Would be really great to have your excellent module merged!

@len-foss
Copy link
Copy Markdown
Contributor Author

Hi @ivs-cetmix done 👍

Thank you @ShehbajTechultra for the suggestions.

@ivs-cetmix
Copy link
Copy Markdown
Member

@ShehbajTechultra could you please do an after-update review? Than you in advance!

Copy link
Copy Markdown

@ShehbajTechultra ShehbajTechultra left a comment

Choose a reason for hiding this comment

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

Approved

@ivs-cetmix
Copy link
Copy Markdown
Member

Approved

@ShehbajTechultra , perfect, thank you!
@len-foss could you please prepare the PR for merge?

@len-foss
Copy link
Copy Markdown
Contributor Author

@ivs-cetmix do you mean you prefer to have the commits squashed?

@ivs-cetmix
Copy link
Copy Markdown
Member

@len-foss , yes, we need to squash commits so we have two - one per module.

@len-foss len-foss force-pushed the 18.0-web_json_widget-len branch from 9fa5534 to d700636 Compare August 26, 2025 18:51
@ivs-cetmix
Copy link
Copy Markdown
Member

Hey @OCA/web-maintainers looks like we have a new cool widget ready to join the OCA family. Would appreciate your review)

Copy link
Copy Markdown
Member

@hbrunn hbrunn left a comment

Choose a reason for hiding this comment

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

# content from https://app.unpkg.com/jsoneditor@10.2.0/files/dist
# source: https://github.com/josdejong/jsoneditor?tab=readme-ov-file
# licenced under Apache-2.0 license
"/web_json_widget/static/src/lib/dark-theme.css",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

those usually go into /static/lib

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

// Improvements: add supportedOptions, ExtractProps...
};

registry.category("fields").add("json_editor", JsonEditor);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd suggest

Suggested change
registry.category("fields").add("json_editor", JsonEditor);
registry.category("fields").add("json", JsonEditor);

because then Odoo should pick the widget automatically, given there's not standard json widget

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

this.resId = this.props.record.resId;
this.resModel = this.props.record.resModel;
if (this.editor) {
this.editor.set(this.props.record.data[this.props.name] || {});
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
this.editor.set(this.props.record.data[this.props.name] || {});
this.editor.set(this.state.value);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So, this one is pretty bad. Because of the very stupid OCA rule I squashed my commits, but basically if your recordset is dirty, using the record switcher makes it so that none of the widget lifecycle hooks are called. So, this.state.value is not updated, so you switch and the widget is not updated. I couldn't find anything that seemed to make sense and the odoo dev I asked was a clueless. This is inspired by how it's done by the mail JS code.

To clarify, onParched is a lifecycle hook but that is called continuously, whereas you'd want a 1-time one like onWillUpdateProps.

So, if there's a good explanation and solution, I'd like to know it.


const isDarkMode = this.detect_dark_mode();
if (isDarkMode) {
this.editorRef.el.classList.add("json-editor-theme-dark");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder why you don't do this in the template?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure exactly but the doc about the Json editor was a bit confusing to me, and I found confusing answers as to how to instantiate a dark version. I think the 'Odoo way' is to put that in a CSS without the dark mangling which is put into the dark assets bundle. As of now I find it simpler to keep it this way because I copy-pasted this from recommended setup, but it wouldn't be hard to change afterwards.

onChange: () => {
try {
const new_value = this.editor.get();
this.state.value = {...new_value};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

isn't it the point of useState that you don't need to update this manually? Actually, what use is the state proxy in this widget in the first place?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right, it's not needed at all. I started by copying a basic widget, it's a 'PoC' remnant.

@len-foss
Copy link
Copy Markdown
Contributor Author

@hbrunn
Regarding the test, in my opinion, it would be much more cost much more that what it's worth, so I won't implement it on my limited free time.
You're free to disagree of course, but I'm seeing test_web_systray_button_init_action make the tests red for everything, so consider the time lost for all (potential) contributors.

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.

6 participants