Skip to content

Commit

Permalink
feat: implement OnyxAlertDialog component (#2329)
Browse files Browse the repository at this point in the history
Relates to #2306

Implement `OnyxAlertDialog` component
  • Loading branch information
larsrickert authored Dec 17, 2024
1 parent 62ee690 commit b2a99a9
Show file tree
Hide file tree
Showing 47 changed files with 272 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/thirty-jobs-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sit-onyx": minor
---

feat: implement `OnyxAlertDialog` component
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ONYX_BREAKPOINTS } from "@sit-onyx/shared/breakpoints";
import { expect, test } from "../../playwright/a11y";
import TestWrapperCt from "./TestWrapper.ct.vue";

test.describe("Screenshot tests", () => {
for (const breakpoint of ["2xs", "xs", "sm"] as const) {
test(`Screenshot ${breakpoint}`, async ({ mount, makeAxeBuilder, page }) => {
await page.setViewportSize({ width: ONYX_BREAKPOINTS[breakpoint] + 1, height: 256 });

// ARRANGE
await mount(<TestWrapperCt />);

// ASSERT
await expect(page).toHaveScreenshot(`breakpoint-${breakpoint}.png`);

// ACT
const accessibilityScanResults = await makeAxeBuilder().analyze();

// ASSERT
expect(accessibilityScanResults.violations).toEqual([]);
});
}
});

test("should behave correctly", async ({ mount }) => {
let closeEventCount = 0;

// ARRANGE
const component = await mount(<TestWrapperCt onClose={() => closeEventCount++} />);
const closeButton = component.getByRole("button", { name: "Close dialog" });

// ACT
await closeButton.click();

// ASSERT
expect(closeEventCount).toBe(1);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import circleAttention from "@sit-onyx/icons/circle-attention.svg?raw";
import type { Meta, StoryObj } from "@storybook/vue3";
import { h, ref, watchEffect } from "vue";
import OnyxButton from "../OnyxButton/OnyxButton.vue";
import OnyxAlertDialog from "./OnyxAlertDialog.vue";

/**
* The alert dialog is used to provide important information to the user.
* Note that this dialog is an [alert dialog](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/).
* It interrupts the user's workflow to communicate an important message and acquires a response, e.g. a delete confirmation.
*
* You can also implement a custom dialog using the [OnyxDialog](/docs/support-dialog--docs) component.
*/
const meta: Meta<typeof OnyxAlertDialog> = {
title: "Feedback/AlertDialog",
component: OnyxAlertDialog,
argTypes: {
default: { control: { type: "text" } },
},
};

export default meta;
type Story = StoryObj<typeof OnyxAlertDialog>;

export const Default = {
args: {
label: "Confirm deletion",
default:
"Are you sure that you want to delete the selected item? This action can not be reverted.",
icon: {
icon: circleAttention,
color: "danger",
},
actions: [
h(OnyxButton, { label: "Cancel", color: "neutral", mode: "plain", autofocus: true }),
h(OnyxButton, { label: "Delete", color: "danger" }),
],
},
decorators: [
(story, ctx) => ({
components: { story, OnyxButton },
setup: () => {
const isOpen = ref(false);
watchEffect(() => {
ctx.args.open = isOpen.value;
});
return { isOpen };
},
template: `<div>
<OnyxButton label="Show alert modal" @click="isOpen = true" />
<story :open="isOpen" @close="isOpen = false;" />
</div>`,
}),
],
} satisfies Story;
129 changes: 129 additions & 0 deletions packages/sit-onyx/src/components/OnyxAlertDialog/OnyxAlertDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<script lang="ts" setup>
import xSmall from "@sit-onyx/icons/x-small.svg?raw";
import { useDensity } from "../../composables/density";
import { injectI18n } from "../../i18n";
import OnyxDialog from "../OnyxDialog/OnyxDialog.vue";
import OnyxHeadline from "../OnyxHeadline/OnyxHeadline.vue";
import OnyxIcon from "../OnyxIcon/OnyxIcon.vue";
import OnyxSystemButton from "../OnyxSystemButton/OnyxSystemButton.vue";
import type { OnyxAlertDialogProps } from "./types";
const props = defineProps<OnyxAlertDialogProps>();
const emit = defineEmits<{
/**
* Emitted when the dialog should be closed.
*/
close: [];
}>();
defineSlots<{
/**
* Dialog content.
*/
default(): unknown;
/**
* Optional slot to override the headline with custom content.
*/
headline?(bindings: Pick<OnyxAlertDialogProps, "label">): unknown;
/**
* Slot to display custom actions at the bottom of the dialog, e.g. buttons for confirm or cancelling the current user workflow.
* For accessibility purposes it is recommended to set autofocus on one button, preferably the "cancel" button if one exists.
*
* @example
* ```vue
* <OnyxButton label="Cancel" color="neutral" mode="plain" autofocus />
* ```
*/
actions?(): unknown;
}>();
const { t } = injectI18n();
const { densityClass } = useDensity(props);
</script>

<template>
<OnyxDialog
:class="['onyx-alert-dialog', densityClass]"
v-bind="props"
modal
alert
@close="emit('close')"
>
<div class="onyx-alert-dialog__content">
<OnyxIcon v-if="props.icon" class="onyx-alert-dialog__icon" v-bind="props.icon" size="64px" />

<div>
<div class="onyx-alert-dialog__headline">
<slot name="headline" :label="props.label">
<OnyxHeadline is="h2">{{ props.label }}</OnyxHeadline>
</slot>

<OnyxSystemButton
class="onyx-alert-dialog__close"
:label="t('dialog.close')"
:icon="xSmall"
@click="emit('close')"
/>
</div>

<div class="onyx-alert-dialog__body onyx-truncation">
<slot></slot>
</div>
</div>
</div>

<div class="onyx-alert-dialog__actions">
<slot name="actions"></slot>
</div>
</OnyxDialog>
</template>

<style lang="scss">
@use "../../styles/mixins/layers.scss";
@use "../../styles/breakpoints.scss";
.onyx-alert-dialog {
@include layers.component() {
--max-width: 26rem;
&__content {
max-width: var(--max-width);
display: flex;
align-items: center;
gap: var(--onyx-density-sm);
}
&__headline {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--onyx-density-xs);
}
&__body {
margin-top: var(--onyx-density-sm);
white-space: pre-line;
}
&__actions {
margin-top: var(--onyx-density-md);
display: flex;
align-items: center;
justify-content: flex-end;
gap: var(--onyx-density-xs);
max-width: var(--max-width);
}
&__icon {
@include breakpoints.screen(max, md) {
--icon-size: 3rem;
}
@include breakpoints.screen(max, sm) {
--icon-size: 2rem;
}
}
}
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script lang="ts" setup>
import circleAttention from "@sit-onyx/icons/circle-attention.svg?raw";
import OnyxButton from "../OnyxButton/OnyxButton.vue";
import OnyxAlertDialog from "./OnyxAlertDialog.vue";
const emit = defineEmits<{
close: [];
}>();
</script>

<template>
<OnyxAlertDialog
label="Confirm deletion"
:icon="{ icon: circleAttention, color: 'danger' }"
open
@close="emit('close')"
>
Are you sure that you want to delete the selected item? This action can not be reverted.

<template #actions>
<OnyxButton label="Cancel" color="neutral" autofocus />
<OnyxButton label="Delete" color="danger" />
</template>
</OnyxAlertDialog>
</template>
9 changes: 9 additions & 0 deletions packages/sit-onyx/src/components/OnyxAlertDialog/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { OnyxDialogProps } from "../OnyxDialog/types";
import type { OnyxIconProps } from "../OnyxIcon/types";

export type OnyxAlertDialogProps = Omit<OnyxDialogProps, "modal" | "alert"> & {
/**
* Optional icon to show.
*/
icon?: Omit<OnyxIconProps, "size">;
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import OnyxDialog from "./OnyxDialog.vue";
* If using the `modal` mode, the primary page content will not be interactive.
*/
const meta: Meta<typeof OnyxDialog> = {
title: "Feedback/Dialog",
title: "Support/Dialog",
component: OnyxDialog,
argTypes: {
default: { control: { type: "text" } },
Expand Down
3 changes: 2 additions & 1 deletion packages/sit-onyx/src/components/OnyxDialog/OnyxDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ watch(
border-radius: var(--onyx-radius-md);
font-family: var(--onyx-font-family);
color: var(--onyx-color-text-icons-neutral-intense);
padding: var(--onyx-density-lg);
padding: var(--onyx-density-md) var(--onyx-density-lg);
background-color: var(--onyx-color-base-background-blank);
overflow: auto;
z-index: var(--onyx-z-index-page-overlay);
Expand All @@ -104,6 +104,7 @@ watch(

&:modal {
z-index: var(--onyx-z-index-app-overlay);
box-shadow: var(--onyx-shadow-soft-bottom);
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions packages/sit-onyx/src/i18n/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,8 @@
},
"input": {
"clear": "Eingabe löschen"
},
"dialog": {
"close": "Dialog schließen"
}
}
3 changes: 3 additions & 0 deletions packages/sit-onyx/src/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,8 @@
},
"input": {
"clear": "Clear input"
},
"dialog": {
"close": "Close dialog"
}
}
3 changes: 3 additions & 0 deletions packages/sit-onyx/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import "./styles/index.scss";

export { ONYX_BREAKPOINTS, type OnyxBreakpoint } from "@sit-onyx/shared/breakpoints";

export { default as OnyxAlertDialog } from "./components/OnyxAlertDialog/OnyxAlertDialog.vue";
export * from "./components/OnyxAlertDialog/types";

export { default as OnyxAppLayout } from "./components/OnyxAppLayout/OnyxAppLayout.vue";
export * from "./components/OnyxAppLayout/types";

Expand Down

0 comments on commit b2a99a9

Please sign in to comment.