8000 fix: Fix regressions in `Field`. by gonfunko · Pull Request #9011 · google/blockly · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

fix: Fix regressions in Field. #9011

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

Merged
merged 1 commit into from
May 13, 2025
Merged

Conversation

gonfunko
Copy link
Contributor
@gonfunko gonfunko commented May 7, 2025

The basics

The details

Resolves

Fixes #9005

Proposed Changes

This PR fixes several regressions in Field introduced by an earlier change:

  • It restores the Field.NBSP constant, which was in fact in use
  • It makes empty input fields have a reasonable minimum width
  • It prevents sequential spaces from collapsing when rendering a field

@gonfunko gonfunko requested a review from cpcallen May 7, 2025 20:44
@gonfunko gonfunko requested a review from a team as a code owner May 7, 2025 20:44
@github-actions github-actions bot added the PR: fix Fixes a bug label May 7, 2025
Comment on lines +117 to +137
/** This field's dimensions. */
private size: Size = new Size(0, 0);

/**
* Gets the size of this field. Because getSize() and updateSize() have side
* effects, this acts as a shim for subclasses which wish to adjust field
* bounds when setting/getting the size without triggering unwanted rendering
* or other side effects. Note that subclasses must override *both* get and
* set if either is overridden; the implementation may just call directly
* through to super, but it must exist per the JS spec.
*/
protected get size_(): Size {
return this.size;
}

/**
* Sets the size of this field.
*/
protected set size_(newValue: Size) {
this.size = newValue;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Styleguide says "At least one accessor for a property must be non-trivial: do not define "pass-through" accessors only for the purpose of hiding a property."

I gather you are doing this in order to be able to write super.size_ = newValue in FieldInput's get size_ accessor, but I think a better approach might be to have updateSize_ enforce a minimum width (possibly in Field but preferably in FieldInput). (Alas, updateSize_ isn't written in a way that makes it easy to override for this particular purpose.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As you noted, updateSize_() is not well-suited to being overridden, and short of introducing a setSizeWithoutSideEffects() method I couldn't see a way to refactor it to be more amendable. Likewise, overriding getSize() (or calling it from updateSize_() instead of accessing the size_ property directly) is problematic due to its side effects of rerendering the field. Hence this seemed like the least-bad option, as it preserves backwards compatibility and allows for subclasses to adjust the size as they need. If you have an idea for how updateSize_() might be refactored I'd certainly be open to that though!

Copy link
Contributor
@cpcallen cpcallen May 9, 2025

Choose a reason for hiding this comment

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

I had to think about this for a bit, but I think I see a way to do this without needing to have accessors on Field.

The obvious approach is to have the get and set accessors on the subclasses that want to enforce a minimum width only, and leave Field alone. As you doubtless noticed, the reason this doesn't work is that the Field constructor sets .size_, and that results in (e.g.) FieldInput instances having a .size_ property that is found before the get size_/set size_ accessors on FieldInput.prototype.

But if the Field constructor either didn't set that property if there were accessors, like so:

class FieldInput {
  constructor() {
    this.size_ ??= new Size(0, 0);
  }
}

or if the FieldInput constructor removed it, like so:

class FieldInput {
  constructor() {
    super();
    delete this.size_;
  }
}

then I think it's possible to have the behaviour you have here without the mostly-useless accessors on Field.prototype. Both approaches are a bit kludgy, and the delete approach might have some performance consequences (it's generally preferable not to change the shape of an object, though here at least it's being done at construction time). I'm genuinely not sure either is clearly preferable to your proposal, but I offer them for your consideration.

Copy link
Contributor

Choose a reason for hiding this comment

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

Additionally, I think that even if you keep the get/set accessors on Field it might be preferable to not call them from the corresponding accessors on FieldInput, either by making size protected (so that that FieldInput's accessors can use it directly) or by having FieldInput keep it's own separate Size property. A pair of accessors ought to be interchangeable with a data property, in my view, but you have revealed that that isn't always the case. I'd like to at least not tie ourselves too tightly to using accessors everywhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer the mostly-useless accessors (which most fields can just ignore) over the kludgy approaches mentioned above.

@cpcallen
Copy link
Contributor
cpcallen commented May 8, 2025

(Restoration of .NBSP and space-to-NBSP anti-collapse code all LGTM, though.)

protected size_: Size;

/** This field's dimensions. */
private size: Size = new Size(0, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Style guide suggests something like sizeInternal:

Suggested change
private size: Size = new Size(0, 0);
private sizeInternal: Size = new Size(0, 0);

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 confused about this, why? it's already private, why would we also add the word internal

Copy link
Contributor

Choose a reason for hiding this comment

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

Quoting the Styleguide section on get and set accesors says:

If an accessor is used to hide a class property, the hidden property may be prefixed or suffixed with any whole word, like internal or wrapped.

They do not provide a rational, but I suspect the reason is to avoid clashing with the name of the accessors. In this case the accessors are named size_ (which, with the trailing underscore, is contrary to current naming policy) but if it were size then the internal property would need to be named something else.

@maribethb maribethb self-assigned this May 13, 2025
Comment on lines +117 to +137
/** This fi 8000 eld's dimensions. */
private size: Size = new Size(0, 0);

/**
* Gets the size of this field. Because getSize() and updateSize() have side
* effects, this acts as a shim for subclasses which wish to adjust field
* bounds when setting/getting the size without triggering unwanted rendering
* or other side effects. Note that subclasses must override *both* get and
* set if either is overridden; the implementation may just call directly
* through to super, but it must exist per the JS spec.
*/
protected get size_(): Size {
return this.size;
}

/**
* Sets the size of this field.
*/
protected set size_(newValue: Size) {
this.size = newValue;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer the mostly-useless accessors (which most fields can just ignore) over the kludgy approaches mentioned above.

protected size_: Size;

/** This field's dimensions. */
private size: Size = new Size(0, 0);
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 confused about this, why? it's already private, why would we also add the word internal

@gonfunko gonfunko merged commit 14e1ef6 into google:rc/v12.0.0 May 13, 2025
11 checks passed
@gonfunko gonfunko deleted the field-regression branch May 13, 2025 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: fix Fixes a bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0