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

feat: notification component #741

Merged
merged 24 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1a323ed
feat(notification): implement notification base
ogunb Nov 14, 2023
03fe8d2
refactor(notification): popover to display above dialog
ogunb Nov 14, 2023
22e5397
revert: notification revert popover changes
ogunb Nov 15, 2023
36730ba
refactor(notification): card hides itself on close now
ogunb Nov 15, 2023
62dd5d3
test(notification): implement notification tests
ogunb Nov 15, 2023
431e38b
refactor(alert): action slot handling should flatten slots
ogunb Nov 19, 2023
2eb528f
refactor(notification): rename slots and replace danger variant with …
ogunb Nov 19, 2023
809ddb4
refactor: extract getMiddleOfElement util
ogunb Nov 19, 2023
bee906f
test(notification): implement card tests
ogunb Nov 19, 2023
2344e8a
build: storybook build fix
ogunb Nov 20, 2023
4cb52fc
refactor(dialog): use polyfilled by default
ogunb Nov 20, 2023
ba2bfd6
test(notification): refactor event tests
ogunb Nov 20, 2023
14986bc
docs(notification): implement notification docs
ogunb Nov 21, 2023
cc018a3
chore: fix lint error
ogunb Nov 21, 2023
0c2561b
chore: fix chromatic screenshots
ogunb Nov 21, 2023
2cb12c0
fix(notification): slide out from top animation
ogunb Nov 21, 2023
cd33f1a
fix(alert): display issue preventing close
ogunb Nov 21, 2023
b02cce1
fix(notification): card jumps down after swipe
ogunb Nov 22, 2023
24b3501
docs(notification): implement notification control
ogunb Nov 23, 2023
ddddf95
refactor(notification): disable touchmove down
ogunb Nov 23, 2023
13fc3ef
docs(notification): add card without actions
ogunb Nov 23, 2023
7dc4667
docs(notification): showcase duration variants
ogunb Nov 24, 2023
4ed0de6
docs(notification): secondary example should include primary
ogunb Nov 24, 2023
cd22d6a
test(notification): refactor assertions
ogunb Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 28 additions & 27 deletions commitlint.config.cjs
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
extends: ["@commitlint/config-conventional"],
rules: {
'scope-enum': [
"scope-enum": [
2,
'always',
"always",
[
'storybook',
'design',
'react',
'deps',
'deps-dev',
"storybook",
"design",
"react",
"deps",
"deps-dev",
// Components as scopes listed below
'button',
'icon',
'input',
'badge',
'tab',
'tooltip',
'progress-indicator',
'checkbox-group',
'checkbox',
'alert',
'select',
'pagination',
'radio',
'dialog',
'drawer',
'dropdown',
'switch',
'textarea',
'popover',
"button",
"icon",
"input",
"badge",
"tab",
"tooltip",
"progress-indicator",
"checkbox-group",
"checkbox",
"alert",
"select",
"pagination",
"radio",
"dialog",
"drawer",
"dropdown",
"switch",
"textarea",
"popover",
"notification",
],
],
},
Expand Down
2 changes: 2 additions & 0 deletions src/baklava.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ export { default as BlDropdown } from "./components/dropdown/bl-dropdown";
export { default as BlDropdownItem } from "./components/dropdown/item/bl-dropdown-item";
export { default as BlDropdownGroup } from "./components/dropdown/group/bl-dropdown-group";
export { default as BlSwitch } from "./components/switch/bl-switch";
export { default as BlNotification } from "./components/notification/bl-notification";
export { default as BlNotificationCard } from "./components/notification/card/bl-notification-card";
export { getIconPath, setIconPath } from "./utilities/asset-paths";
17 changes: 15 additions & 2 deletions src/components/alert/bl-alert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,25 @@ describe("Slot", () => {
const el = await fixture<typeofBlAlert>(
html`<bl-alert>
<bl-button slot="action"> Action Slot </bl-button>
<span slot="action">Should not render this element</span>
<bl-button slot="action-secondary"> Action Slot </bl-button>
</bl-alert>`
);
const actionSlot = el.shadowRoot?.querySelector('slot[name="action"]');

expect(actionSlot).to.exist;
const actionSlot = el.shadowRoot!.querySelector('slot[name="action"]') as HTMLSlotElement;
const [actionElement, actionSpanElement] = actionSlot.assignedElements();

expect(actionElement as HTMLElement).to.exist;
expect(actionElement.tagName).to.eq("BL-BUTTON");
expect(actionSpanElement as HTMLElement).to.not.exist;

const actionSecondarySlot = el.shadowRoot!.querySelector(
'slot[name="action-secondary"]'
) as HTMLSlotElement;
const actionSecondaryElement = actionSecondarySlot?.assignedElements()[0] as HTMLElement;

expect(actionSecondaryElement).to.exist;
expect(actionSecondaryElement.tagName).to.eq("BL-BUTTON");
});
it("renders `action` slot empty when bl-button is not used", async () => {
const el = await fixture<typeofBlAlert>(
Expand Down
6 changes: 3 additions & 3 deletions src/components/alert/bl-alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,17 @@ export default class BlAlert extends LitElement {

private _initAlertActionSlot(event: Event) {
const slotElement = event.target as HTMLSlotElement;
const slotElements = slotElement.assignedElements();
const slotElements = slotElement.assignedElements({ flatten: true });

slotElements.forEach(element => {
if (element.tagName !== "BL-BUTTON") {
element.parentNode?.removeChild(element);
return;
}

(slotElement.parentElement as HTMLElement).style.display = "flex";
(this.shadowRoot?.querySelector(".actions") as HTMLElement).style.display = "flex";

const variant = element.slot === "action-secondary" ? "secondary" : "primary";
const variant = slotElement.name === "action-secondary" ? "secondary" : "primary";
const buttonTypes: Record<AlertVariant, string> = {
info: "default",
warning: "neutral",
Expand Down
8 changes: 8 additions & 0 deletions src/components/dialog/bl-dialog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ document.querySelector("bl-dialog").addEventListener("bl-dialog-request-close",
});
```

## Using HTML Dialog Element

Starting from version 2.4.0 of Baklava, the `bl-dialog` component uses a polyfill implementation by default due to some quirks in the native HTML Dialog element.
This change was prompted by issues encountered during the development of the `bl-notification` component, where the native dialog element blocked any actions on it, event with Popover attribute.

If you wish to use the native HTML Dialog element, you can do so by setting the `polyfilled` attribute to `false`.
Please note that this will cause notifications to render under the dialog backdrop. Prior to version 2.4.0, this was the default behavior.

## Reference

<ArgsTable of="bl-dialog" />
Expand Down
6 changes: 3 additions & 3 deletions src/components/dialog/bl-dialog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe("bl-dialog", () => {
window.HTMLDialogElement = htmlDialogElement;
});
it("should render html dialog component with the default values when supports html dialog", async () => {
const el = await fixture<typeOfBlDialog>(html`<bl-dialog></bl-dialog>`);
const el = await fixture<typeOfBlDialog>(html`<bl-dialog .polyfilled=${false}></bl-dialog>`);

assert.shadowDom.equal(
el,
Expand Down Expand Up @@ -132,7 +132,7 @@ describe("bl-dialog", () => {
});

it("should close the dialog when the close btn is clicked", async () => {
const el = await fixture<typeOfBlDialog>(html` <bl-dialog open caption="My title">
const el = await fixture<typeOfBlDialog>(html` <bl-dialog .polyfilled=${false} open caption="My title">
<div>My Content</div>
</bl-dialog>`);
const dialog = el.shadowRoot?.querySelector(".dialog") as HTMLDivElement;
Expand Down Expand Up @@ -340,7 +340,7 @@ describe("bl-dialog", () => {
});

it("should fire bl-dialog-request-close event when dialog closes by clicking backdrop", async () => {
const el = await fixture<typeOfBlDialog>(html`<bl-dialog open caption="My title">
const el = await fixture<typeOfBlDialog>(html`<bl-dialog .polyfilled=${false} open caption="My title">
</bl-dialog>`);

const dialog = el?.shadowRoot?.querySelector<HTMLDialogElement>(".dialog");
Expand Down
12 changes: 10 additions & 2 deletions src/components/dialog/bl-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,17 @@ export default class BlDialog extends LitElement {
* using polyfill by setting this to true in some cases like to show some content on top of dialog
* in case you are not able to use Popover API. Be aware that, polyfilled version can cause some
* inconsistencies in terms of accessibility and stacking context. So use it with extra caution.
*
* As of the current implementation, you can render above the dialog HTML element using the Popover API. However,
* it will block any actions on the Popover element. This issue was encountered during the development of the `bl-notification` component.
* As a result, we decided to enable the polyfill for the `bl-dialog` component by default. If you prefer to use the native dialog, you can set
* this property to false. Please note, doing so will cause notifications to render under the dialog backdrop.
* For more information, refer to the comment linked below:
*
* https://github.com/Trendyol/baklava/issues/141#issuecomment-1810301413
*/
@property({ type: Boolean, reflect: true })
polyfilled = !window.HTMLDialogElement;
polyfilled = true;

@query(".dialog")
private dialog: HTMLDialogElement & DialogElement;
Expand Down Expand Up @@ -176,7 +184,7 @@ export default class BlDialog extends LitElement {
}

render(): TemplateResult {
return this.polyfilled
return this.polyfilled || !window.HTMLDialogElement
? html`<div
class="dialog-polyfill"
role="dialog"
Expand Down
106 changes: 106 additions & 0 deletions src/components/notification/bl-notification.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
:host {
--bl-notification-width: 396px;
--bl-notification-gap: var(--bl-size-2xs);
--bl-notification-animation-duration: 0.3s;
}

.wrapper {
--margin: var(--bl-size-xl);

display: flex;
flex-direction: column-reverse;
position: fixed;
top: 0;
right: 0;
max-width: var(--bl-notification-width);
margin: var(--margin);
width: calc(100% - var(--margin) * 2);
z-index: var(--bl-index-notification);
}

@media screen and (max-width: 480px) {
.wrapper {
flex-direction: column;
max-width: 100%;
}
}

.notification {
will-change: transform height margin;
animation: slide-in-right var(--bl-notification-animation-duration) ease;
margin-bottom: var(--bl-notification-gap);
touch-action: none;
}

.notification[data-slide="top"] {
animation: slide-in-top var(--bl-notification-animation-duration) ease;
}

:host([no-animation]) .notification {
animation: none;
}

.notification.removing {
animation: slide-out-right var(--bl-notification-animation-duration) ease forwards,
size-to-zero var(--bl-notification-animation-duration) ease
var(--bl-notification-animation-duration) forwards;
}

.notification[data-slide="top"].removing {
animation: slide-out-top var(--bl-notification-animation-duration) ease forwards,
size-to-zero var(--bl-notification-animation-duration) ease
var(--bl-notification-animation-duration) forwards;
}

:host([no-animation]) .notification.removing {
animation: size-to-zero 0;
}

@media (prefers-reduced-motion) {
.notification.removing {
animation: size-to-zero 0;
}

.notification {
animation: none;
}
}

@keyframes slide-in-right {
from {
transform: translateX(var(--travel-distance, 10px));
height: 0;
opacity: 0;
margin: 0;
}
}

@keyframes slide-out-right {
to {
transform: translateX(var(--travel-distance, 10px));
opacity: 0;
}
}

@keyframes slide-in-top {
from {
transform: translateY(var(--travel-distance, -10px));
height: 0;
opacity: 0;
margin: 0;
}
}

@keyframes slide-out-top {
to {
transform: translateY(var(--travel-distance, -10px));
opacity: 0;
}
}

@keyframes size-to-zero {
to {
height: 0;
margin: 0;
}
}
Loading
Loading