Skip to content

feat(expansion): add accordion expand/collapse all (#6929) #7461

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

Conversation

henetiriki
Copy link
Contributor

Add expandAll()/collapseAll() to perform expand/collapse all on multiple expandable accordions.

Closes #6929

@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Oct 2, 2017
@henetiriki henetiriki force-pushed the feature/6929-accordion-expand-collapse-all branch from 835b6fb to 1a41208 Compare October 2, 2017 05:52
Copy link
Contributor

@amcdnl amcdnl left a comment

Choose a reason for hiding this comment

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

Looks good overall, just a few nit.

/**
* Expands all enabled expansion panels in an accordion where multi is enabled
*/
expandAll() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add return type void

/**
* Collapses all enabled expansion panels in an accordion where multi is enabled
*/
collapseAll() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add return type void

this._expandCollapseAll(false);
}

_expandCollapseAll(expanded: boolean) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add return type void


@Component({
moduleId: module.id,
selector: 'expansion-demo',
styleUrls: ['expansion-demo.css'],
templateUrl: 'expansion-demo.html',
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
preserveWhitespaces: true,
Copy link
Member

Choose a reason for hiding this comment

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

This needs to stay false

@@ -25,7 +26,10 @@ export class CdkAccordion {
/** A readonly id value to use for unique selection coordination. */
readonly id = `cdk-accordion-${nextId++}`;

/** Whether the accordion should allow multiple expanded accordion items simulateously. */
/** Stream that emits when expandAll or collapseAll is triggered. */
readonly expandCollapseAllSubject: Subject<boolean> = new Subject<boolean>();
Copy link
Member

Choose a reason for hiding this comment

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

Mark this with an underscore to denote that it is internal-only and add a JsDoc description (specifically that the boolean denotes expanded/collapsed)

// When a panel is hosted in an accordion, subscribe to the expand/collapse subject
if (this.panel.accordion) {
this._expandCollapseAllSubscription =
this.panel.accordion.expandCollapseAllSubject.subscribe((expanded: boolean) => {
Copy link
Member

Choose a reason for hiding this comment

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

The type for expanded doesn't need to be given here because it is inferred from the observable.


// When a panel is hosted in an accordion, subscribe to the expand/collapse subject
if (this.panel.accordion) {
this._expandCollapseAllSubscription =
Copy link
Member

Choose a reason for hiding this comment

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

Can you move the contents of this if block to a separate method? Something like _subscribeToAccordionActions

@josephperrott josephperrott self-assigned this Oct 2, 2017
@@ -101,6 +102,17 @@ export class MatExpansionPanelHeader implements OnDestroy {
.subscribe(() => this._changeDetectorRef.markForCheck());

_focusMonitor.monitor(_element.nativeElement, renderer, false);

// When a panel is hosted in an accordion, subscribe to the expand/collapse subject
if (this.panel.accordion) {
Copy link
Member

Choose a reason for hiding this comment

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

By placing the logic for setting panel expansion state in the MatExpansionPanelHeader it ties the logic specifically to the Material implementation rather than the CDK base.

I would suggestion adding this logic to the AccordionItem class.

There you can set the expanded state directly.

Copy link
Contributor Author

@henetiriki henetiriki Oct 2, 2017

Choose a reason for hiding this comment

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

I need access to the disabled state of the hosting panel to prevent disabled panels from being expanded/collapsed, which is why I used _toggle(). Is there another way to get access to this property without too much refactoring?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Think I may have solved it with mixinDisabled - let me know if that is not a desired solution though. Cheers.

Copy link
Member

Choose a reason for hiding this comment

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

We actually just removed the mixinDisabled from AccordionItem as we are moving it to CDK. You can see the move in #7530

I supposed you could bake the expandAll/collapseAll into the AccordionItem, and then in MatExpansionPanel, extend the setter of expanded to respect disabled. This would allow the expandAll/collapseAll functionality to live in the CDK, but work as expected in MatAccordion. Admittedly I haven't thought this idea entirely through and would need to consider the ramifications a bit more before saying its definitely the best option or the option to go with.

Copy link
Contributor Author

@henetiriki henetiriki Oct 9, 2017

Choose a reason for hiding this comment

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

Thanks, @josephperrott - I will wait for #7530 to be merged and then take it from there.

// Only toggle if current state is not the intended state
if (!!this._isExpanded() !== expanded) {
this._toggle();
}
Copy link
Member

Choose a reason for hiding this comment

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

Coupled with the move to AccordionItem, I would suggest just setting the expanded state directly rather than checking if a toggle needs to occur.

this.panel.accordion.expandCollapseAllSubject.subscribe(expanded => this.expanded = expanded);

@@ -44,6 +48,26 @@ export class CdkAccordion {
* elevation.
*/
@Input() displayMode: MatAccordionDisplayMode = 'default';

/**
* Expands all enabled expansion panels in an accordion where multi is enabled
Copy link
Member

Choose a reason for hiding this comment

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

JsDoc comments should be complete sentences so add a . here?

@henetiriki
Copy link
Contributor Author

Thanks for the feedback @amcdnl, @jelbourn and @josephperrott - will get this sorted asap.

Unable to figure out if the Travis Sauce Labs failures are just platform problems or issues with this ticket.

Also, do I need to take any action on the Screenshot Tests?

@josephperrott
Copy link
Member

@ouq77 We have been having some issues with our saucelab tests, I don't think its from you. For the screenshot tests, no need to worrry about them unless they are we are seeing failures in the expansion panels.

Once we get to a LGTM state on this PR, I can make the screenshot tests as good, however I want to wait as it will reset after every new push to the branch.

@henetiriki henetiriki force-pushed the feature/6929-accordion-expand-collapse-all branch 2 times, most recently from 9c95e7b to 1bd3b61 Compare October 6, 2017 04:00
@henetiriki henetiriki force-pushed the feature/6929-accordion-expand-collapse-all branch 6 times, most recently from 18f0593 to e4dca02 Compare October 16, 2017 22:55
@henetiriki
Copy link
Contributor Author

@josephperrott I attempted your suggestion of adding the functionality in the CDK, but couldn't find an elegant way of accessing the disabled property on the accordion item.

Since this is mainly a Material concern, leaving it in this layer is probably acceptable.

Let me know if this is not an ideal and I can rethink it.

Is there any chance that the common-behaviours will be moved to the CDK?

@josephperrott
Copy link
Member

I spoke with @jelbourn and there does not seem to be an issue with moving some of the common-behaviors into CDK. disabled as an example would make sense to do so. We might need to move disabled and CanDisable into CDK which would open up all of the logic for expandAll/collapseAll to be in CDK.

@henetiriki
Copy link
Contributor Author

Is this something I can help with @josephperrott? Should probably be done against a separate issue?

@josephperrott
Copy link
Member

@ouq77 You definitely can move it to the CDK if you would like. It should be done in another pull ideally.

@jelbourn
Copy link
Member

@josephperrott should this PR be revisited?

@henetiriki
Copy link
Contributor Author

henetiriki commented Jan 24, 2018

@josephperrott and @jelbourn - this PR is blocked until (and will be reworked after) #8201 has been dealt with. I've been keeping that one up to date hoping it will be revisited soon. Cheers.

@josephperrott josephperrott added blocked This issue is blocked by some external factor, such as a prerequisite PR and removed blocked This issue is blocked by some external factor, such as a prerequisite PR labels Jan 24, 2018
@josephperrott
Copy link
Member

PR is now unblocked with the merge of #8201

@henetiriki henetiriki force-pushed the feature/6929-accordion-expand-collapse-all branch 3 times, most recently from 23cc812 to 1b2aaa8 Compare January 26, 2018 22:19
@henetiriki
Copy link
Contributor Author

Knew I forgot something - updated now :)

@henetiriki henetiriki force-pushed the feature/6929-accordion-expand-collapse-all branch from 1b2aaa8 to 0bff53f Compare January 26, 2018 22:55
Copy link
Member

@josephperrott josephperrott left a comment

Choose a reason for hiding this comment

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

Just the one comment, then it looks good to me. Though the change to naming will likely have some precipitating effects.

@@ -28,4 +32,20 @@ export class CdkAccordion {
get multi(): boolean { return this._multi; }
set multi(multi: boolean) { this._multi = coerceBooleanProperty(multi); }
private _multi: boolean = false;

/** Expands all enabled accordion items in an accordion where multi is enabled. */
expandAll(): void {
Copy link
Member

Choose a reason for hiding this comment

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

These should likely be openAll and closeAll to match the naming convention used in CdkAccordionItem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@henetiriki henetiriki force-pushed the feature/6929-accordion-expand-collapse-all branch 3 times, most recently from 12ab88a to 12e74a9 Compare January 26, 2018 23:36
Copy link
Member

@josephperrott josephperrott left a comment

Choose a reason for hiding this comment

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

LGTM

@@ -125,4 +134,13 @@ export class CdkAccordionItem implements OnDestroy {
this.expanded = true;
}
}

private _subscribeToExpandCollapseAllActions(): Subscription {
Copy link
Member

Choose a reason for hiding this comment

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

This method should likely be called subscribetoOpenCloseAllActions()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

this._expandCollapseAll(false);
}

private _expandCollapseAll(expanded: boolean): void {
Copy link
Member

Choose a reason for hiding this comment

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

This should be _openCloseAll()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Adds openAll()/closeAll() to perform expand/collapse all on multiple expandable accordions.

Closes angular#6929
@henetiriki henetiriki force-pushed the feature/6929-accordion-expand-collapse-all branch from 12e74a9 to 7c2e39e Compare January 29, 2018 17:58
@josephperrott josephperrott added pr: lgtm action: merge The PR is ready for merge by the caretaker target: minor This PR is targeted for the next minor release and removed pr: needs rebase labels Jan 29, 2018
}

private _openCloseAll(expanded: boolean): void {
if (this.multi) {
Copy link
Contributor

@SchnWalter SchnWalter Jan 30, 2018

Choose a reason for hiding this comment

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

There are some cases, even for [multi]="false", where you don't care which CdkAccordionItem is open, you just want to make sure that all are closed and it would be nice to also cover that use case, but I'm not sure how this could be done without confusing developers.

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 guess you could do this.multi || !expanded? Will have to experiment.

Copy link
Contributor

@SchnWalter SchnWalter Jan 31, 2018

Choose a reason for hiding this comment

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

That would be a solution, but we could have a "cleaner" solution, if I may use this word.

This would be a good solution for this particular case. But maybe we could find a more generic solution that could be used in other cases and not confuse people to much, openAll() won't work in single mode, but we could have open() that works for single mode, opening the first item or in multi mode.

If you look at something like the SelectionModel for tables, select and chips, the selection has a getMultipleValuesInSingleSelectionError, we could use something like that. And MatSelectionList.selectedOptions exposes the model for anyone to use. But probably a model would be an overkill for this situation, I'm not sure in how many places we could reuse it.

Also, MatDrawerContainer container has a similar behavior but it doesn't add the All suffix and it uses @ContentChildren to keep a list of children and then call open/close on each children, instead of emitting events, here's a snippet from it:

  @ContentChildren(MatDrawer) _drawers: QueryList<MatDrawer>;

  /** Calls `open` of both start and end drawers */
  open(): void {
    this._drawers.forEach(drawer => drawer.open());
  }

  /** Calls `close` of both start and end drawers */
  close(): void {
    this._drawers.forEach(drawer => drawer.close());
  }

Using ContentChildren instead of emitting an event and rename to open/close in order to support "multi/single" mode, would be great.

P.S. Thanks for the work you did on this PR!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This maybe warrants a separate issue to investigate?

@mmalerba mmalerba merged commit 3aceb73 into angular:master Feb 9, 2018
@henetiriki henetiriki deleted the feature/6929-accordion-expand-collapse-all branch February 9, 2018 22:30
@pankajsri03
Copy link

I created a demo using the example in the demo-app , but expand-all/collapse-all isn't working.
https://stackblitz.com/edit/angular-jmitne?file=app/expansion-overview-example.html.
This gives the error _co.accordion.openAll is not a function

@henetiriki
Copy link
Contributor Author

@pankajsri03 - you are using cdk and material v5.2.1 in your example. This change will only be available in v6. (It is currently in beta)

@zijianhuang
Copy link

zijianhuang commented May 17, 2018

@pankajsri03 and @ouq77
I am using NG6.0.2 and NGMD6.0.2, and see the same "openAll is not a function".

"dependencies": { "@angular/animations": "^6.0.2", "@angular/cdk": "^6.0.2", "@angular/common": "^6.0.2", "@angular/compiler": "^6.0.2", "@angular/compiler-cli": "^6.0.2", "@angular/core": "^6.0.2", "@angular/flex-layout": "^6.0.0-beta.15", "@angular/forms": "^6.0.2", "@angular/material": "^6.0.2", "@angular/material-moment-adapter": "^6.0.2", "@angular/platform-browser": "^6.0.2", "@angular/platform-browser-dynamic": "^6.0.2", "@angular/platform-server": "^6.0.2", "@angular/router": "^6.0.2",

                    <mat-accordion #consultsAccordion multi="true">
                        <consult-item *ngFor="let c of consults" [model]="c" [filter]="filter"></consult-item>
                    </mat-accordion>
    @ViewChild('consultsAccordion') consultPanels: MatAccordion;
...
    this.consultPanels.openAll();

@zijianhuang
Copy link

As of NGMD 6.2.1, the problem persist: "openAll is not a function". Do I miss something? or someone in the NGMD team had forgotten to merge?

@henetiriki
Copy link
Contributor Author

@zijianhuang I assume your consult-item component contains the mat-expansion-panel? I may be wrong, but for any of the expansion panel logic to work as expected, they need to be hosted directly inside the mat-accordion.

@zijianhuang
Copy link

@ouq77, yes, the root element of consult-item is
<mat-expansion-panel (opened)="handleOpened()" (closed)="handleClosed()">

So, after NG rendering, mat-expansion-panel is hosted directly inside the mat-accordion.

@zijianhuang
Copy link

In the other parts of the program, I do have mat-expansion-panel directly inside the mat-accordion, however, openalll is still not function.

After a few NG and MD releases, this is still not fixed. Now I just go with my own workaround or solution: publish an event, and all expansion panels subscribe it; when the parent component fire the event, all subscribers just open the expansion panels.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement target: minor This PR is targeted for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[mdExpansionPanel] Expand/collapse all functionality
9 participants