diff --git a/.changeset/ninety-spoons-design.md b/.changeset/ninety-spoons-design.md new file mode 100644 index 0000000000..88af2ede2a --- /dev/null +++ b/.changeset/ninety-spoons-design.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-documentation': minor +'@swisspost/design-system-components': minor +--- + +Added a `delayed` property to the `post-tooltip` component to allow delaying its display for a few milliseconds after it is triggered. diff --git a/packages/components/src/components.d.ts b/packages/components/src/components.d.ts index c4a14afe4f..ba024f8726 100644 --- a/packages/components/src/components.d.ts +++ b/packages/components/src/components.d.ts @@ -280,6 +280,10 @@ export namespace Components { * Wheter or not to display a little pointer arrow */ "arrow"?: boolean; + /** + * If `true`, the tooltip is displayed a few milliseconds after it is triggered + */ + "delayed": boolean; /** * Programmatically hide this tooltip */ @@ -736,6 +740,10 @@ declare namespace LocalJSX { * Wheter or not to display a little pointer arrow */ "arrow"?: boolean; + /** + * If `true`, the tooltip is displayed a few milliseconds after it is triggered + */ + "delayed"?: boolean; /** * Defines the placement of the tooltip according to the floating-ui options available at https://floating-ui.com/docs/computePosition#placement. Tooltips are automatically flipped to the opposite side if there is not enough available space and are shifted towards the viewport if they would overlap edge boundaries. */ diff --git a/packages/components/src/components/post-tooltip/post-tooltip.tsx b/packages/components/src/components/post-tooltip/post-tooltip.tsx index 68de40a449..5a7f2fe549 100644 --- a/packages/components/src/components/post-tooltip/post-tooltip.tsx +++ b/packages/components/src/components/post-tooltip/post-tooltip.tsx @@ -1,9 +1,12 @@ -import { Component, Element, h, Host, Method, Prop } from '@stencil/core'; +import { Component, Element, h, Host, Method, Prop, Watch } from '@stencil/core'; import { Placement } from '@floating-ui/dom'; import { version } from '@root/package.json'; import isFocusable from 'ally.js/is/focusable'; import 'long-press-event'; import { getAttributeObserver } from '@/utils/attribute-observer'; +import { checkEmptyOrType, timeout } from '@/utils'; + +const OPEN_DELAY = 650; // matches HTML title delay /** * @slot default - Slot for the content of the tooltip. @@ -110,6 +113,24 @@ export class PostTooltip { */ @Prop() readonly arrow?: boolean = true; + /** + * If `true`, the tooltip is displayed a few milliseconds after it is triggered + */ + @Prop() readonly delayed: boolean = false; + + @Watch('delayed') + validateDelayed() { + checkEmptyOrType( + this.delayed, + 'boolean', + 'The post-tooltip "delayed" property should be a boolean.', + ); + } + + connectedCallback() { + this.validateDelayed(); + } + componentDidLoad() { if (!this.host.id) { throw new Error( @@ -168,6 +189,7 @@ export class PostTooltip { */ @Method() async show(target: HTMLElement) { + if (this.delayed) await timeout(OPEN_DELAY); this.popoverRef.show(target); } diff --git a/packages/components/src/components/post-tooltip/readme.md b/packages/components/src/components/post-tooltip/readme.md index cb015f8702..215b2f07f9 100644 --- a/packages/components/src/components/post-tooltip/readme.md +++ b/packages/components/src/components/post-tooltip/readme.md @@ -10,6 +10,7 @@ | Property | Attribute | Description | Type | Default | | ----------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `arrow` | `arrow` | Wheter or not to display a little pointer arrow | `boolean` | `true` | +| `delayed` | `delayed` | If `true`, the tooltip is displayed a few milliseconds after it is triggered | `boolean` | `false` | | `placement` | `placement` | Defines the placement of the tooltip according to the floating-ui options available at https://floating-ui.com/docs/computePosition#placement. Tooltips are automatically flipped to the opposite side if there is not enough available space and are shifted towards the viewport if they would overlap edge boundaries. | `"bottom" \| "bottom-end" \| "bottom-start" \| "left" \| "left-end" \| "left-start" \| "right" \| "right-end" \| "right-start" \| "top" \| "top-end" \| "top-start"` | `'top'` | diff --git a/packages/components/src/utils/index.ts b/packages/components/src/utils/index.ts index 6756aaf369..52d8b3df1e 100644 --- a/packages/components/src/utils/index.ts +++ b/packages/components/src/utils/index.ts @@ -1,3 +1,4 @@ export * from './property-checkers'; export * from './is-motion-reduced'; export * from './sass-export'; +export * from './timeout'; diff --git a/packages/components/src/utils/timeout.ts b/packages/components/src/utils/timeout.ts new file mode 100644 index 0000000000..47a7d1f5dd --- /dev/null +++ b/packages/components/src/utils/timeout.ts @@ -0,0 +1,3 @@ +export function timeout(delay: number): Promise { + return new Promise(resolve => setTimeout(resolve, delay)); +} diff --git a/packages/documentation/src/stories/components/tooltip/tooltip.stories.ts b/packages/documentation/src/stories/components/tooltip/tooltip.stories.ts index a7a9c6b8a4..2bae9646a4 100644 --- a/packages/documentation/src/stories/components/tooltip/tooltip.stories.ts +++ b/packages/documentation/src/stories/components/tooltip/tooltip.stories.ts @@ -23,6 +23,7 @@ const meta: MetaComponent = { innerHTML: 'Hi there 👋', backgroundColor: 'primary', placement: 'top', + delayed: false, }, argTypes: { id: { @@ -88,6 +89,7 @@ function render(args: Args) { class="hydrated bg-${args.backgroundColor}" placement="${ifDefined(args.placement)}" arrow="${ifDefined(args.arrow)}" + delayed="${ifDefined(args.delayed)}" > ${unsafeHTML(innerHTML)} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b002e6bf0..478c920c3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11512,7 +11512,7 @@ snapshots: '@vitejs/plugin-basic-ssl': 1.1.0(vite@5.1.7(@types/node@20.12.7)(less@4.2.0)(sass@1.71.1)(terser@5.29.1)) ansi-colors: 4.1.3 autoprefixer: 10.4.18(postcss@8.4.35) - babel-loader: 9.1.3(@babel/core@7.24.0)(webpack@5.90.3) + babel-loader: 9.1.3(@babel/core@7.24.0)(webpack@5.90.3(esbuild@0.20.1)) babel-plugin-istanbul: 6.1.1 browserslist: 4.23.0 copy-webpack-plugin: 11.0.0(webpack@5.90.3(esbuild@0.20.1)) @@ -11606,7 +11606,7 @@ snapshots: '@vitejs/plugin-basic-ssl': 1.1.0(vite@5.1.7(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(terser@5.29.1)) ansi-colors: 4.1.3 autoprefixer: 10.4.18(postcss@8.4.35) - babel-loader: 9.1.3(@babel/core@7.24.0)(webpack@5.90.3) + babel-loader: 9.1.3(@babel/core@7.24.0)(webpack@5.90.3(esbuild@0.20.1)) babel-plugin-istanbul: 6.1.1 browserslist: 4.23.0 copy-webpack-plugin: 11.0.0(webpack@5.90.3(esbuild@0.20.1)) @@ -17255,7 +17255,7 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@9.1.3(@babel/core@7.24.0)(webpack@5.90.3): + babel-loader@9.1.3(@babel/core@7.24.0)(webpack@5.90.3(esbuild@0.20.1)): dependencies: '@babel/core': 7.24.0 find-cache-dir: 4.0.0