Skip to content

feat(material-experimental/snack-bar): add MDC-based snack-bar #19738

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 8 commits into from
Jun 29, 2020

Conversation

andrewseguin
Copy link
Contributor

No description provided.

@andrewseguin andrewseguin added P2 The issue is important to a large percentage of users, with a workaround merge safe target: patch This PR is targeted for the next patch release labels Jun 23, 2020
@andrewseguin andrewseguin requested a review from a team as a code owner June 23, 2020 22:57
@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Jun 23, 2020
Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

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

Did you have to touch the tests much, or are they copied pretty much identically (barring CSS class changes)?

Comment on lines 19 to 44
/** Directive that should be applied to the text element to be rendered in the snack bar. */
@Directive({
selector: `[matSnackBarLabel]`,
host: {
'class': 'mat-mdc-snack-bar-label mdc-snackbar__label',
}
})
export class MatSnackBarLabel {}

/** Directive that should be applied to the element containing the snack bar's action buttons. */
@Directive({
selector: `[matSnackBarActions]`,
host: {
'class': 'mat-mdc-snack-bar-actions mdc-snackbar__actions',
}
})
export class MatSnackBarActions {}

/** Directive that should be applied to each of the snack bar's action buttons. */
@Directive({
selector: `[matSnackBarAction]`,
host: {
'class': 'mat-mdc-snack-bar-action mdc-snackbar__action',
}
})
export class MatSnackBarAction {}
Copy link
Member

Choose a reason for hiding this comment

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

Move these directive to their own file like snack-bar-content.ts?

(side note, I regret deeply that we have "snack-bar" instead of "snackbar")

encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class.mat-mdc-simple-snack-bar]': 'true',
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this needs to be a binding unless I'm forgetting some edge case

* Internal interface for a simple snack bar component..
* @docs-private
*/
export interface MatSimpleSnackBarInterface {
Copy link
Member

Choose a reason for hiding this comment

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

We generally want to avoid suffixing with "Interface"

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'm at a loss for names. I'd like to call it SimpleSnackBar and let the implementations be MatSimpleSnackBar, but our legacy snack bar is already named SimpleSnackBar

Copy link
Contributor Author

Choose a reason for hiding this comment

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

BaseSimpleSnackBar?

// tslint:disable-next-line:validate-decorators
changeDetection: ChangeDetectionStrategy.Default,
encapsulation: ViewEncapsulation.None,
animations: [],
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't seem to be used.

// specifically the MDC label. If not, the container should apply the MDC label class to this
// component's label container, which will apply MDC's label styles to the attached view.
const attachedViewHasMdcLabel =
!!this._label.nativeElement.querySelector('.mdc-snackbar__label');
Copy link
Member

@crisbeto crisbeto Jun 24, 2020

Choose a reason for hiding this comment

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

This is being run on every change detection cycle so it's very performance sensitive. Can we avoid querying the DOM and deduce the class some other way?

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 is my least favorite part of the implementation, but I'm not sure how to get around it. As far as I can tell, it'll be difficult for the container to know whether the user provided a label and action using the MDC classes (either directly or with our directives).

I'd like to do this once, but I suspect we'll see bugs where the user provides something wrapped in an ngIf or ng-template that is loaded dynamically. Perhaps there could be a compromise where we perform this once at initialization, and allow for an API that lets the user explicitly say that their content will dynamically have the MDC classes

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can match it with a directive and query it with a ContentChild?

Copy link
Member

Choose a reason for hiding this comment

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

That seems like it would work

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The user-provided component or template doesn't get included as part of either the content or view of the snack bar container. Technically I think the portal could provide this info through its view container - is it possible to make the query on the container's behalf?

Copy link
Member

Choose a reason for hiding this comment

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

Ah, I see, I forgot it was in a component (my brain spaced on this being snackbar).

Since snackbar content is generally simple and because they're short-lived, this is probably fine.

// component's label container, which will apply MDC's label styles to the attached view.
const attachedViewHasMdcLabel =
!!this._label.nativeElement.querySelector('.mdc-snackbar__label');
this._label.nativeElement.classList.toggle('mdc-snackbar__label', !attachedViewHasMdcLabel);
Copy link
Member

Choose a reason for hiding this comment

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

IE doesn't support the second argument for classList.toggle.

export class MatSnackBarContainer extends BasePortalOutlet
implements MatSnackBarContainerInterface, AfterViewChecked, OnDestroy {
/** Subject for notifying that the snack bar has exited from view. */
readonly _onExit: Subject<any> = new Subject();
Copy link
Member

Choose a reason for hiding this comment

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

Should we type these subjects as void?

// Mark this element with an 'exit' attribute to indicate that the snackbar has
// been dismissed and will soon be removed from the DOM. This is used by the snackbar
// test harness.
this._elementRef.nativeElement.setAttribute('mat-exit', '');
Copy link
Member

Choose a reason for hiding this comment

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

You should be able to set this through the host binding.

this._elementRef.nativeElement.setAttribute('mat-exit', '');

this._mdcFoundation.close();
return this._onExit;
Copy link
Member

Choose a reason for hiding this comment

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

Should this have an asObservable call so we don't expose the subject?


/** Makes sure the exit callbacks have been invoked when the element is destroyed. */
ngOnDestroy() {
this._mdcFoundation.close();
Copy link
Member

Choose a reason for hiding this comment

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

Should we also destroy the foundation here? AFAIK we create a new container each time a snack bar is opened.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added it to the adapter's notifyClosed method. Here in ngOnDestroy is too early for it to be destroyed

@andrewseguin
Copy link
Contributor Author

@jelbourn Tests were copied/pasted over, with the removal of tests for the no-longer-needed _animationState

* Internal interface for a simple snack bar component..
* @docs-private
*/
export interface BaseSimpleSnackBar {
Copy link
Member

Choose a reason for hiding this comment

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

How about something like TextOnlySnackbar or MessageOnlySnackbar

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Works for me, I think BasicSnackBar could also work, but it's docs-private anyways and I'm not too worried about it. Went with TextOnlySnackBar

@andrewseguin andrewseguin requested a review from jelbourn June 25, 2020 17:18
@@ -53,6 +56,16 @@ export declare class MatSnackBarContainer extends BasePortalOutlet implements On
static ɵfac: i0.ɵɵFactoryDef<MatSnackBarContainer, never>;
}

export interface MatSnackBarContainerInterface {
Copy link
Member

Choose a reason for hiding this comment

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

I missed this interface earlier. Maybe just SnackbarContainer?

@@ -92,3 +105,11 @@ export declare class SimpleSnackBar {
static ɵcmp: i0.ɵɵComponentDefWithMeta<SimpleSnackBar, "simple-snack-bar", never, {}, {}, never, never>;
static ɵfac: i0.ɵɵFactoryDef<SimpleSnackBar, never>;
}

export interface TextOnlySnackBar {
Copy link
Member

Choose a reason for hiding this comment

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

Thinking about this more, could we prefix the newly added interfaces with an underscore? Maybe worth discussing in the team meeting, but I want to start making it more clear that some symbols are internal aside from just not documenting them.

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

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

LGTm, you can add merge-ready when ready

@jelbourn jelbourn added the lgtm label Jun 25, 2020
@andrewseguin andrewseguin force-pushed the mdc-snack-bar branch 2 times, most recently from a1e3be5 to f53b10f Compare June 26, 2020 14:56
@andrewseguin andrewseguin added the action: merge The PR is ready for merge by the caretaker label Jun 26, 2020
@jelbourn jelbourn merged commit a63bfc5 into angular:master Jun 29, 2020
jelbourn pushed a commit that referenced this pull request Jun 29, 2020
* feat(material-experimental/snack-bar): add MDC-based snack-bar

* review changes

* review changes 2

* fix import

* api golden

* review changes

* remove underscore prefix

* golden
jelbourn added a commit to jelbourn/components that referenced this pull request Jun 29, 2020
angular#19738)"

This reverts commit a63bfc5.

This PR was marked as merge-safe, but it actually changed the existing
snackbar.
jelbourn added a commit that referenced this pull request Jun 29, 2020
#19738)" (#19799)

This reverts commit a63bfc5.

This PR was marked as merge-safe, but it actually changed the existing
snackbar.
jelbourn added a commit that referenced this pull request Jun 29, 2020
#19738)" (#19799)

This reverts commit a63bfc5.

This PR was marked as merge-safe, but it actually changed the existing
snackbar.
@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 Jul 30, 2020
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 P2 The issue is important to a large percentage of users, with a workaround target: patch This PR is targeted for the next patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants