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

refactor(accordion, accordion-item): getElementProp on child is factored out and parent gets a mutation observer to update children #6055

Conversation

Elijbet
Copy link
Contributor

@Elijbet Elijbet commented Dec 15, 2022

Related Issue: #6038, #5698

Summary

getElementProp on child is factored out and parent gets a mutation observer to update children.

…ut and parent gets a mutation observer to update children
…ementProp-and-get-mutation-observer-on-parent
@github-actions github-actions bot added the bug Bug reports for broken functionality. Issues should include a reproduction of the bug. label Dec 15, 2022
@Elijbet
Copy link
Contributor Author

Elijbet commented Dec 15, 2022

I implemented this new pattern to just the scale property, this is necessary to move ahead with #5698.
I can merge this as is with just scale and continue working on accordion-item for #5698, and make a follow-up issue to address all other properties that need to be inherited.

Or I can put all other properties that need to be inherited in this PR as well since most of the work is done. I would just need to know which additional ones need to be inherited (appearance, iconPosition, iconType).

Added "[Erik/Eliza]: Pattern Discussion: Parent <> Child Property Relationships" for the next meeting.

@driskull @eriklharper

@Elijbet Elijbet changed the title fix(accordion, accordion-item): getElementProp on child is factored out and parent gets a mutation observer to update children fix(accordion, accordion-item): getElementProp on child is factored out and parent gets a mutation observer to update children Dec 15, 2022
@Elijbet Elijbet changed the title fix(accordion, accordion-item): getElementProp on child is factored out and parent gets a mutation observer to update children refactor(accordion, accordion-item): getElementProp on child is factored out and parent gets a mutation observer to update children Dec 15, 2022

private passPropsToAccordionItems = (): void => {
(
Array.from(
Copy link
Contributor

@macandcheese macandcheese Dec 18, 2022

Choose a reason for hiding this comment

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

Can we use the existing this.items instead of querying for the child items again? I suppose this.items may not always be populated at connectedCallback but should be in componentDidLoad

componentDidLoad(): void {
     ...
    this.passPropsToAccordionItems();
  }

and

private passPropsToAccordionItems = (): void => {
    this.items.forEach((el) => (el.scale = this.scale));
};

Seems to work reliably.

Copy link
Contributor

Choose a reason for hiding this comment

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

This makes sense as long as this.items gets updated when accordion-items get added or removed from the light dom. Is this currently handled in any way?

Copy link
Contributor

@macandcheese macandcheese Dec 19, 2022

Choose a reason for hiding this comment

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

Yeah good point the internal registration events are kind of an outdated pattern and wouldn't respond - slot change observer that responds to addition / removal of children and updates this.items is probably better. There's a handful of our ... more senior 👴👵 ... components that could probably benefit from a change like that.

await page.waitForChanges();

expect(await accordion.getProperty("scale")).toEqual(scale.l);
expect(await item1.getProperty("scale")).toEqual(scale.m);
Copy link
Contributor

@macandcheese macandcheese Dec 18, 2022

Choose a reason for hiding this comment

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

Don't we want the scale of calcite-accordion and calcite-accordion-item to match in this case - shouldn't let folks set scale "m" on the child if the parent is "l". (assuming scale on calcite-accordion-item is internal property)

Copy link
Contributor Author

@Elijbet Elijbet Dec 19, 2022

Choose a reason for hiding this comment

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

Seems logical that if calcite-accordion-item is an internal property we shouldn't be letting users set it to a different scale, but based on other comments looks like this is also part of tomorrow's Child Property Relationships discussion.

Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we want the scale of calcite-accordion and calcite-accordion-item to match in this case - shouldn't let folks set scale "m" on the child if the parent is "l". (assuming scale on calcite-accordion-item is internal property)

I agree it seems odd to allow this, but if you think about it, this pattern breaks component autonomy. When a component defines its own scale prop, I as a developer expect it to honor whatever value I'm explicitly setting it to, excepting the default fallback value. If a parent value comes in and overrides that value I've explicitly set on the child, the property pattern on that child component no longer behaves according to standard convention.

Copy link
Contributor

@macandcheese macandcheese Dec 19, 2022

Choose a reason for hiding this comment

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

Yeah, although if the property is internal and is set by / managed by the parent maybe I wouldn't expect to be able to set this at all, as its undocumented. Though to follow through on that the scale probably shouldn't have the default to "m" until it receives the scale from calcite-accordion.

Copy link
Contributor

Choose a reason for hiding this comment

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

This same principle applies to object inheritance in programming. If I have Object A that defines a foo property, an Object B that extends Object A, I don't expect setting the foo property on Object A to override what I've explicitly set it to on Object B. I expect the lowest level of specificity on that property to win above all others.

Copy link
Contributor

@macandcheese macandcheese Dec 19, 2022

Choose a reason for hiding this comment

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

Right - but its not a public property (or, is proposed to be an undocumented, internal property) - so the test case of it being set in that way should not be valid?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, although if the property is internal and is set by / managed by the parent maybe I wouldn't expect to be able to set this at all, as its undocumented. Though to follow through on that the scale probably shouldn't have the default to "m" until it receives the scale from calcite-accordion.

For sure. I think with a component like accordion, its children items are so tightly interwoven with the parent that this pattern does make sense where you would explicitly control children properties like scale from the parent level. It gets trickier when you have child components that can live standalone by themselves like radio-button and tile-select where they need to work on their own regardless if their corresponding parent wrapper component is being used.

Right - but its not a public property (or, is proposed to not be an undocumented, internal property) - so the test case of it being set in that way should not be valid?

Agreed. I think if we mark these properties as internal, and call out in our documentation that modifying internal properties is not supported, then this pattern can be adopted for certain parent/child component systems.

expect(await item1.getProperty("scale")).toEqual(scale.s);

const icon1: E2EElement = await page.find(`calcite-accordion-item[id='1'] >>> .${CSS.iconStart}`);
expect(await icon1.getProperty("scale")).toEqual(scale.m);
Copy link
Contributor

@macandcheese macandcheese Dec 18, 2022

Choose a reason for hiding this comment

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

Should we instead expect this icon be a scale s based on the scale s of the parent calcite-accordion-item?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should probably add this to tomorrow's Pattern Discussion: Parent <> Child Property Relationships. From what I understand while we expect a parent to update children where these 2 have a parent/child relationship (as in accordion and its item), element and its slotted icon don't have that relationship and are expected to have independent scales. But in this particular case, it does feel a bit odd. Erik also brought up that we should not override an item with the parent if a user sets it individually to a different scale, although I'm not sure what the use case for that would be.

Copy link
Contributor

@eriklharper eriklharper Dec 19, 2022

Choose a reason for hiding this comment

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

Erik also brought up that we should not override an item with the parent if a user sets it individually to a different scale, although I'm not sure what the use case for that would be.

Not sure what the specific use case would be either, but I still think that if the scale property is explicitly applied either as an attribute or by setting the property directly on the child component, that this value should be honored and not overridden by the parent component. We can actively discourage doing this in the documentation, and encourage users to set the value at the parent level to ensure visual consistency, but I don't think we should break from convention with regards to how property values are honored at the individual component level.

@github-actions
Copy link
Contributor

This PR has been automatically marked as stale because it has not had recent activity. Please close your PR if it is no longer relevant. Thank you for your contributions.

@github-actions github-actions bot added the Stale Issues or pull requests that have not had recent activity. label Dec 27, 2022
@Elijbet Elijbet closed this Jul 8, 2023
@Elijbet
Copy link
Contributor Author

Elijbet commented Jul 8, 2023

Closing this, as it was resolved in #7191.

@Elijbet Elijbet deleted the elijbet/6038-accordion-item-refactor-getElementProp-and-get-mutation-observer-on-parent branch February 29, 2024 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Bug reports for broken functionality. Issues should include a reproduction of the bug. Stale Issues or pull requests that have not had recent activity.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants