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(core/toast): add position top-right #432

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 38 additions & 0 deletions packages/angular-test-app/src/preview-examples/toast-position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2023 Siemens AG
*
* SPDX-License-Identifier: MIT
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import { Component, TemplateRef, ViewChild } from '@angular/core';
import { ToastService } from '@siemens/ix-angular';

@Component({
selector: 'app-example',
template: `
<div>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
amet.
</div>
<ix-button (click)="showToastMessage()">Show Toast</ix-button>
`,
})
export default class Toast {
@ViewChild('customToast', { read: TemplateRef })
customModalRef!: TemplateRef<any>;

constructor(private readonly toastService: ToastService) {}

async showToastMessage() {
this.toastService.setPosition('top-right');
this.toastService.show({
message: 'Hello World!',
});
}
}
16 changes: 14 additions & 2 deletions packages/angular/src/toast/toast.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,26 @@
*/

import { Injectable } from '@angular/core';
import { toast, ToastConfig as IxToastConfig } from '@siemens/ix';
import {
getToastContainer,
toast,
ToastConfig as IxToastConfig,
} from '@siemens/ix';
import { ToastConfig } from './toast.config';

@Injectable({
providedIn: 'root',
})
export class ToastService {
public async show(config: ToastConfig) {
setPosition(position: 'bottom-right' | 'top-right') {
getToastContainer().position = position;
}

getPosition() {
return getToastContainer().position;
}

async show(config: ToastConfig) {
if (typeof config.message === 'string') {
return toast(config as IxToastConfig);
}
Expand Down
7 changes: 6 additions & 1 deletion packages/core/component-doc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8243,7 +8243,7 @@
},
{
"name": "position",
"type": "string",
"type": "\"bottom-right\" | \"top-right\"",
"mutable": false,
"attr": "position",
"reflectToAttr": false,
Expand All @@ -8252,6 +8252,11 @@
"default": "'bottom-right'",
"values": [
{
"value": "bottom-right",
"type": "string"
},
{
"value": "top-right",
"type": "string"
}
],
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1395,7 +1395,7 @@ export namespace Components {
interface IxToastContainer {
"containerClass": string;
"containerId": string;
"position": string;
"position": 'bottom-right' | 'top-right';
/**
* Display a toast message
* @param config
Expand Down Expand Up @@ -3743,7 +3743,7 @@ declare namespace LocalJSX {
interface IxToastContainer {
"containerClass"?: string;
"containerId"?: string;
"position"?: string;
"position"?: 'bottom-right' | 'top-right';
}
interface IxToggle {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
position: fixed;
}

.toast-container--top-right {
right: 1rem;
top: 2rem;
}

.toast-container--bottom-right {
right: 1rem;
bottom: 2rem;
Expand Down
27 changes: 24 additions & 3 deletions packages/core/src/components/toast/toast-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@
* LICENSE file in the root directory of this source tree.
*/

import { Component, Element, h, Host, Method, Prop } from '@stencil/core';
import {
Component,
Element,
h,
Host,
Method,
Prop,
Watch,
} from '@stencil/core';
import { TypedEvent } from '../utils/typed-event';
import { ToastConfig } from './toast-utils';

Expand All @@ -28,7 +36,9 @@ export class ToastContainer {

/**
*/
@Prop() position = 'bottom-right';
@Prop() position: 'bottom-right' | 'top-right' = 'bottom-right';

private readonly PREFIX_POSITION_CLASS = 'toast-container--';

get hostContainer() {
return document.getElementById(this.containerId);
Expand All @@ -39,11 +49,22 @@ export class ToastContainer {
const toastContainer = document.createElement('div');
toastContainer.id = this.containerId;
toastContainer.classList.add(this.containerClass);
toastContainer.classList.add(`toast-container--${this.position}`);
toastContainer.classList.add(
`${this.PREFIX_POSITION_CLASS}${this.position}`
);
document.body.appendChild(toastContainer);
}
}

@Watch('position')
onPositionChange(newPosition: string, oldPosition: string) {
const toastContainer = document.getElementById(this.containerId);
toastContainer.classList.remove(
`${this.PREFIX_POSITION_CLASS}${oldPosition}`
);
toastContainer.classList.add(`${this.PREFIX_POSITION_CLASS}${newPosition}`);
}

/**
* Display a toast message
* @param config
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/components/toast/toast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

export type ToastType = 'info' | 'success' | 'error' | 'warning';
export type ToastPosition = 'bottom-right' | 'top-right';

export interface ToastConfig {
title?: string;
Expand All @@ -19,7 +20,7 @@ export interface ToastConfig {
iconColor?: string;
}

function getToastContainer() {
export function getToastContainer() {
const containerList = Array.from(
document.querySelectorAll('ix-toast-container')
);
Expand All @@ -39,6 +40,10 @@ function getToastContainer() {
return container;
}

export function setToastPosition(position: ToastPosition) {
getToastContainer().position = position;
}

async function toast(config: ToastConfig) {
const context = getToastContainer();
const toast = await context.showToast(config);
Expand Down
78 changes: 78 additions & 0 deletions packages/core/src/tests/toast/position/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<!--
SPDX-FileCopyrightText: 2023 Siemens AG

SPDX-License-Identifier: MIT
-->

<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0"
/>
<title>Stencil Component Starter</title>
</head>
<body>
<ix-toast-container></ix-toast-container>
<script>
(async () => {
await window.customElements.whenDefined('ix-toast-container');
const toastContainer = document.querySelector('ix-toast-container');

toastContainer.position = 'top-right';

toast = {};
toast.info = (config) => {
toastContainer.showToast({
...config,
autoClose: false,
type: 'info',
});
};

toast.error = (config) => {
toastContainer.showToast({
...config,
autoClose: false,
type: 'error',
});
};

toast.success = (config) => {
toastContainer.showToast({
...config,
autoClose: false,
type: 'success',
});
};

toast.warning = (config) => {
toastContainer.showToast({
...config,
autoClose: false,
type: 'warning',
});
};

toast.info({
message: 'Info',
});
toast.error({
message: 'Error',
});
toast.success({
message: 'Success',
});
toast.warning({
message: 'Warning',
});
toast.info({
icon: 'bulb',
message: 'Custom icon',
});
})();
</script>
<script src="http://127.0.0.1:8080/scripts/e2e/load-e2e-runtime.js"></script>
</body>
</html>
6 changes: 6 additions & 0 deletions packages/core/src/tests/toast/toast.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ regressionTest.describe('toast', () => {
await page.waitForTimeout(200);
expect(await page.screenshot({ fullPage: true })).toMatchSnapshot();
});

regressionTest('position', async ({ page }) => {
await page.goto('toast/position');
await page.waitForTimeout(200);
expect(await page.screenshot({ fullPage: true })).toMatchSnapshot();
});
});
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.
19 changes: 19 additions & 0 deletions packages/documentation/docs/controls/toast.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import { ApiTableSinceTag } from '@site/src/components/ApiTableTag';
import Playground from '@site/src/components/Playground';

import SourceToast from './../auto-generated/previews/web-component/toast.md';
Expand All @@ -12,6 +13,11 @@ import SourceAngularToastCustom from './../auto-generated/previews/angular/toast
import SourceReactToastCustom from './../auto-generated/previews/react/toast-custom.md';
import SourceVueToastCustom from './../auto-generated/previews/vue/toast-custom.md';

import SourceToastPosition from './../auto-generated/previews/web-component/toast-position.md';
import SourceAngularToastPosition from './../auto-generated/previews/angular/toast-position.ts.md';
import SourceReactToastPosition from './../auto-generated/previews/react/toast-position.md';
import SourceVueToastPosition from './../auto-generated/previews/vue/toast-position.md';

import ApiToastConfigJavaScript from './\_toast/javascript/toast-config.md';

import ApiToastServiceAngular from './\_toast/angular/toast-service.html.md';
Expand Down Expand Up @@ -42,6 +48,19 @@ frameworks={{
vue: SourceVueToastCustom
}}></Playground>

## Position
danielleroux marked this conversation as resolved.
Show resolved Hide resolved

<ApiTableSinceTag message="1.5.0" />

<Playground
name="toast-position" height="18rem"
frameworks={{
react: SourceReactToastPosition,
angular: SourceAngularToastPosition,
javascript: SourceToastPosition,
vue: SourceVueToastPosition,
}}></Playground>

## API

<Tabs>
Expand Down
33 changes: 33 additions & 0 deletions packages/html-test-app/src/preview-examples/toast-position.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!--
SPDX-FileCopyrightText: 2023 Siemens AG

SPDX-License-Identifier: MIT
-->

<!DOCTYPE html>
<html>
<head>
<title>Test example</title>
</head>
<body class="theme-brand-dark">
<!-- Preview code -->
<ix-toast-container></ix-toast-container>

<ix-button id="toastButton">Trigger toast</ix-button>
<script type="module">
import { toast, setToastPosition } from '@siemens/ix';

(async function () {
await window.customElements.whenDefined('ix-toast-container');
setToastPosition('top-right');
document.getElementById('toastButton').addEventListener('click', () => {
toast({
message: 'My toast message!',
});
});
})();
</script>
<!-- Preview code -->
<script type="module" src="./init.js"></script>
</body>
</html>
36 changes: 36 additions & 0 deletions packages/react-test-app/src/preview-examples/toast-position.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2023 Siemens AG
*
* SPDX-License-Identifier: MIT
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import { setToastPosition, ToastPosition } from '@siemens/ix';
import { IxButton, showToast } from '@siemens/ix-react';
import React, { useEffect } from 'react';

function useToastPosition(position: ToastPosition) {
useEffect(() => {
setToastPosition(position);
}, []);
}

export default () => {
useToastPosition('top-right');

return (
<>
<IxButton
onClick={() => {
showToast({
message: 'My toast message!',
});
}}
>
Trigger toast
</IxButton>
</>
);
};
Loading