-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
Conversation
f6f243e
to
55e2802
Compare
There was a problem hiding this 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)?
/** 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 {} |
There was a problem hiding this comment.
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', |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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"
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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: [], |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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', ''); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
@jelbourn Tests were copied/pasted over, with the removal of tests for the no-longer-needed |
* Internal interface for a simple snack bar component.. | ||
* @docs-private | ||
*/ | ||
export interface BaseSimpleSnackBar { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
@@ -53,6 +56,16 @@ export declare class MatSnackBarContainer extends BasePortalOutlet implements On | |||
static ɵfac: i0.ɵɵFactoryDef<MatSnackBarContainer, never>; | |||
} | |||
|
|||
export interface MatSnackBarContainerInterface { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this 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
a1e3be5
to
f53b10f
Compare
f53b10f
to
60350a8
Compare
* 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
angular#19738)" This reverts commit a63bfc5. This PR was marked as merge-safe, but it actually changed the existing snackbar.
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
No description provided.