diff --git a/packages/angular-test-app/src/preview-examples/toast-position.ts b/packages/angular-test-app/src/preview-examples/toast-position.ts new file mode 100644 index 00000000000..f7d54a13737 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/toast-position.ts @@ -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: ` +
+ 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. +
+ Show Toast + `, +}) +export default class Toast { + @ViewChild('customToast', { read: TemplateRef }) + customModalRef!: TemplateRef; + + constructor(private readonly toastService: ToastService) {} + + async showToastMessage() { + this.toastService.setPosition('top-right'); + this.toastService.show({ + message: 'Hello World!', + }); + } +} diff --git a/packages/angular/src/toast/toast.service.ts b/packages/angular/src/toast/toast.service.ts index 6fe0313d8ad..7ab1db567d1 100644 --- a/packages/angular/src/toast/toast.service.ts +++ b/packages/angular/src/toast/toast.service.ts @@ -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); } diff --git a/packages/core/component-doc.json b/packages/core/component-doc.json index 2db85c000f6..533a693d83e 100644 --- a/packages/core/component-doc.json +++ b/packages/core/component-doc.json @@ -8243,7 +8243,7 @@ }, { "name": "position", - "type": "string", + "type": "\"bottom-right\" | \"top-right\"", "mutable": false, "attr": "position", "reflectToAttr": false, @@ -8252,6 +8252,11 @@ "default": "'bottom-right'", "values": [ { + "value": "bottom-right", + "type": "string" + }, + { + "value": "top-right", "type": "string" } ], diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index 76dcbf98a8f..c8ab8bf6a2e 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -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 @@ -3743,7 +3743,7 @@ declare namespace LocalJSX { interface IxToastContainer { "containerClass"?: string; "containerId"?: string; - "position"?: string; + "position"?: 'bottom-right' | 'top-right'; } interface IxToggle { /** diff --git a/packages/core/src/components/toast/styles/toast-container.scss b/packages/core/src/components/toast/styles/toast-container.scss index 1f895dbeb1f..c085b0caac5 100644 --- a/packages/core/src/components/toast/styles/toast-container.scss +++ b/packages/core/src/components/toast/styles/toast-container.scss @@ -20,6 +20,11 @@ position: fixed; } +.toast-container--top-right { + right: 1rem; + top: 2rem; +} + .toast-container--bottom-right { right: 1rem; bottom: 2rem; diff --git a/packages/core/src/components/toast/toast-container.tsx b/packages/core/src/components/toast/toast-container.tsx index c0d17886bf6..a4265668242 100644 --- a/packages/core/src/components/toast/toast-container.tsx +++ b/packages/core/src/components/toast/toast-container.tsx @@ -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'; @@ -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); @@ -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 diff --git a/packages/core/src/components/toast/toast-utils.ts b/packages/core/src/components/toast/toast-utils.ts index f289dde82dd..5c40a125995 100644 --- a/packages/core/src/components/toast/toast-utils.ts +++ b/packages/core/src/components/toast/toast-utils.ts @@ -8,6 +8,7 @@ */ export type ToastType = 'info' | 'success' | 'error' | 'warning'; +export type ToastPosition = 'bottom-right' | 'top-right'; export interface ToastConfig { title?: string; @@ -19,7 +20,7 @@ export interface ToastConfig { iconColor?: string; } -function getToastContainer() { +export function getToastContainer() { const containerList = Array.from( document.querySelectorAll('ix-toast-container') ); @@ -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); diff --git a/packages/core/src/tests/toast/position/index.html b/packages/core/src/tests/toast/position/index.html new file mode 100644 index 00000000000..fe470c055f8 --- /dev/null +++ b/packages/core/src/tests/toast/position/index.html @@ -0,0 +1,78 @@ + + + + + + + Stencil Component Starter + + + + + + + diff --git a/packages/core/src/tests/toast/toast.e2e.ts b/packages/core/src/tests/toast/toast.e2e.ts index f615cd0923c..63f455dc81e 100644 --- a/packages/core/src/tests/toast/toast.e2e.ts +++ b/packages/core/src/tests/toast/toast.e2e.ts @@ -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(); + }); }); diff --git a/packages/core/src/tests/toast/toast.e2e.ts-snapshots/toast-position-1-chromium---theme-classic-dark-linux.png b/packages/core/src/tests/toast/toast.e2e.ts-snapshots/toast-position-1-chromium---theme-classic-dark-linux.png new file mode 100644 index 00000000000..075853d3558 Binary files /dev/null and b/packages/core/src/tests/toast/toast.e2e.ts-snapshots/toast-position-1-chromium---theme-classic-dark-linux.png differ diff --git a/packages/core/src/tests/toast/toast.e2e.ts-snapshots/toast-position-1-chromium---theme-classic-light-linux.png b/packages/core/src/tests/toast/toast.e2e.ts-snapshots/toast-position-1-chromium---theme-classic-light-linux.png new file mode 100644 index 00000000000..416fc10b8c5 Binary files /dev/null and b/packages/core/src/tests/toast/toast.e2e.ts-snapshots/toast-position-1-chromium---theme-classic-light-linux.png differ diff --git a/packages/documentation/docs/controls/toast.md b/packages/documentation/docs/controls/toast.md index 24aaae7dc25..0c559dc15d7 100644 --- a/packages/documentation/docs/controls/toast.md +++ b/packages/documentation/docs/controls/toast.md @@ -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'; @@ -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'; @@ -42,6 +48,19 @@ frameworks={{ vue: SourceVueToastCustom }}> +## Position + + + + + ## API diff --git a/packages/html-test-app/src/preview-examples/toast-position.html b/packages/html-test-app/src/preview-examples/toast-position.html new file mode 100644 index 00000000000..55af39d7140 --- /dev/null +++ b/packages/html-test-app/src/preview-examples/toast-position.html @@ -0,0 +1,33 @@ + + + + + + Test example + + + + + + Trigger toast + + + + + diff --git a/packages/react-test-app/src/preview-examples/toast-position.tsx b/packages/react-test-app/src/preview-examples/toast-position.tsx new file mode 100644 index 00000000000..d899f60868d --- /dev/null +++ b/packages/react-test-app/src/preview-examples/toast-position.tsx @@ -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 ( + <> + { + showToast({ + message: 'My toast message!', + }); + }} + > + Trigger toast + + + ); +}; diff --git a/packages/vue-test-app/src/Root.vue b/packages/vue-test-app/src/Root.vue index ac77d4fc6bc..9905ffedba4 100644 --- a/packages/vue-test-app/src/Root.vue +++ b/packages/vue-test-app/src/Root.vue @@ -70,6 +70,7 @@ import Tile from './preview-examples/tile.vue'; import Timepicker from './preview-examples/timepicker.vue'; import ToastCustom from './preview-examples/toast-custom.vue'; import Toast from './preview-examples/toast.vue'; +import ToastPosition from './preview-examples/toast-position.vue'; import ToggleColor from './preview-examples/toggle-color.vue'; import ToggleCustomLabel from './preview-examples/toggle-custom-label.vue'; import ToggleDisabled from './preview-examples/toggle-disabled.vue'; @@ -135,6 +136,7 @@ const routes: any = { '/preview/timepicker': Timepicker, '/preview/toast': Toast, '/preview/toast-custom': ToastCustom, + '/preview/toast-position': ToastPosition, '/preview/toggle': Toggle, '/preview/toggle-disabled': ToggleDisabled, '/preview/toggle-color': ToggleColor, diff --git a/packages/vue-test-app/src/preview-examples/toast-position.vue b/packages/vue-test-app/src/preview-examples/toast-position.vue new file mode 100644 index 00000000000..6b2374ed148 --- /dev/null +++ b/packages/vue-test-app/src/preview-examples/toast-position.vue @@ -0,0 +1,11 @@ + + + diff --git a/packages/vue/src/toast/toast.ts b/packages/vue/src/toast/toast.ts index 424ace54b6e..9b6491db278 100644 --- a/packages/vue/src/toast/toast.ts +++ b/packages/vue/src/toast/toast.ts @@ -7,12 +7,20 @@ * LICENSE file in the root directory of this source tree. */ -import { toast, ToastConfig as IxToastConfig } from '@siemens/ix'; +import { + getToastContainer, + toast, + ToastConfig as IxToastConfig, +} from '@siemens/ix'; export type ToastConfig = { message: string | HTMLElement; }; +export function setToastPosition(position: 'bottom-right' | 'top-right') { + getToastContainer().position = position; +} + export async function showToast( config: Omit & ToastConfig ) {