diff --git a/.changeset/2024-11-10-update-icons.md b/.changeset/2024-11-10-update-icons.md new file mode 100644 index 0000000000..58d8ed8161 --- /dev/null +++ b/.changeset/2024-11-10-update-icons.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-icons': minor +--- + +Added icon number 2632. + diff --git a/.changeset/big-socks-dress.md b/.changeset/big-socks-dress.md new file mode 100644 index 0000000000..5863769099 --- /dev/null +++ b/.changeset/big-socks-dress.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-styles': patch +--- + +Fixed issue where the focus ring was not appearing on inactive chips. diff --git a/.changeset/new-goats-impress.md b/.changeset/new-goats-impress.md new file mode 100644 index 0000000000..d1bd841e3c --- /dev/null +++ b/.changeset/new-goats-impress.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-documentation': minor +'@swisspost/design-system-styles': minor +--- + +Updated vertical-align utility diff --git a/.changeset/popular-mirrors-cross.md b/.changeset/popular-mirrors-cross.md new file mode 100644 index 0000000000..a047d3d7cc --- /dev/null +++ b/.changeset/popular-mirrors-cross.md @@ -0,0 +1,7 @@ +--- +'@swisspost/design-system-components': minor +'@swisspost/design-system-components-angular': minor +'@swisspost/design-system-components-react': minor +--- + +Added a provisional post-header component with some basic functionality in place. This component is not finished in this state. diff --git a/.changeset/pre.json b/.changeset/pre.json index 5169ad951f..a948833b59 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -24,6 +24,7 @@ "changesets": [ "2024-10-17-update-icons", "2024-10-27-update-icons", + "2024-11-10-update-icons", "beige-jobs-do", "big-frogs-admire", "breezy-cups-add", @@ -32,17 +33,25 @@ "cold-baboons-appear", "cold-panthers-vanish", "cuddly-bears-check", + "cuddly-gifts-film", + "dirty-mayflies-taste", "eight-turkeys-matter", "eleven-keys-work", "empty-islands-kneel", "fair-actors-scream", + "fast-bats-poke", "fast-fans-wash", + "fifty-dodos-wait", + "fifty-students-call", "five-hornets-sin", "friendly-insects-breathe", "funny-shrimps-care", "giant-games-swim", + "gold-chairs-grin", + "gold-chefs-rule", "gorgeous-flowers-flow", "great-humans-talk", + "grumpy-parrots-wonder", "heavy-rats-explode", "kind-buses-trade", "kind-papayas-provide", @@ -51,26 +60,33 @@ "loud-dingos-remember", "lovely-deers-itch", "lovely-mirrors-travel", + "neat-suits-provide", "nervous-rocks-shop", + "new-goats-impress", "ninety-nails-float", "pink-weeks-relate", "plenty-apricots-raise", "popular-games-rush", "proud-actors-knock", + "proud-cheetahs-act", "proud-moons-impress", "quick-eagles-watch", "quiet-apes-rhyme", + "rare-dryers-count", "red-cobras-cry", "red-lies-lick", + "rich-timers-listen", "selfish-bats-run", "selfish-ways-know", "shaggy-experts-give", "sharp-baboons-smile", "sharp-crews-watch", "shiny-ears-care", + "shy-walls-exercise", "silver-coins-invent", "six-spiders-smoke", "sixty-items-promise", + "slimy-rockets-pull", "strange-bottles-impress", "tame-terms-push", "three-lies-do", diff --git a/.changeset/quick-buses-give.md b/.changeset/quick-buses-give.md new file mode 100644 index 0000000000..d3212d7400 --- /dev/null +++ b/.changeset/quick-buses-give.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-documentation': minor +'@swisspost/design-system-components': minor +--- + +Added new Menu Button components (post-menu-button, post-menu-trigger, and post-menu-item) for creating accessible dropdown menus. diff --git a/.changeset/shy-walls-exercise.md b/.changeset/shy-walls-exercise.md new file mode 100644 index 0000000000..ba712642c4 --- /dev/null +++ b/.changeset/shy-walls-exercise.md @@ -0,0 +1,7 @@ +--- +'@swisspost/design-system-documentation': minor +'@swisspost/design-system-components': minor +'@swisspost/design-system-styles': minor +--- + +Added close button web component. diff --git a/.github/ISSUE_TEMPLATE/component-v2.yml b/.github/ISSUE_TEMPLATE/component-v2.yml index dfefd7c352..ace2c22cfb 100644 --- a/.github/ISSUE_TEMPLATE/component-v2.yml +++ b/.github/ISSUE_TEMPLATE/component-v2.yml @@ -28,14 +28,31 @@ body: options: - label: Tokens for this component are ready - type: textarea - id: tasks + id: tasksdesign attributes: - label: Tasks + label: Tasks design + description: Default tasks for the design team. Can be edited later according to needs. + value: | + ```[tasklist] + ### 🎨 Design + - [ ] Design component according to WIKIT + - [ ] Update dependencies + - [ ] Add & test compo in layout examples + - [ ] Designer review + - [ ] Documentation: Overview and usage (about, compo overview, compo props, usage, examples) + - [ ] Documentation: Technical documentation (anatomy, accessibility) + - [ ] Documentation: Review (content correctness, understandability, gaps) + ``` + - type: textarea + id: tasksdev + attributes: + label: Tasks development description: Default tasks for the dev team. Can be edited later according to needs. value: | ```[tasklist] - ### 💻 Tasks + ### 💻 Development - [ ] Review Design (All states present? Possible to implement?) + - [ ] Tokenization - [ ] HTML/CSS implementation - [ ] Web component implementation - [ ] Documentation in Storybook diff --git a/packages/components-angular/CHANGELOG.md b/packages/components-angular/CHANGELOG.md index 11eca2d195..14bd6d34d2 100644 --- a/packages/components-angular/CHANGELOG.md +++ b/packages/components-angular/CHANGELOG.md @@ -1,5 +1,13 @@ # @swisspost/design-system-components-angular-workspace +## 1.1.10-next.4 + +### Patch Changes + +- Updated dependencies: + - @swisspost/design-system-styles@9.0.0-next.4 + - @swisspost/design-system-components@9.0.0-next.4 + ## 1.1.10-next.3 ### Patch Changes diff --git a/packages/components-angular/package.json b/packages/components-angular/package.json index 06c7e9eb61..04519844ac 100644 --- a/packages/components-angular/package.json +++ b/packages/components-angular/package.json @@ -1,6 +1,6 @@ { "name": "@swisspost/design-system-components-angular-workspace", - "version": "1.1.10-next.3", + "version": "1.1.10-next.4", "scripts": { "start": "ng serve --port 9210", "build": "ng build components", @@ -18,8 +18,8 @@ "@angular/platform-browser": "18.2.10", "@angular/platform-browser-dynamic": "18.2.10", "@angular/router": "18.2.10", - "@swisspost/design-system-components": "workspace:9.0.0-next.3", - "@swisspost/design-system-styles": "workspace:9.0.0-next.3", + "@swisspost/design-system-components": "workspace:9.0.0-next.4", + "@swisspost/design-system-styles": "workspace:9.0.0-next.4", "rxjs": "7.8.1", "tslib": "2.6.3", "zone.js": "0.14.8" diff --git a/packages/components-angular/projects/components/CHANGELOG.md b/packages/components-angular/projects/components/CHANGELOG.md index c897490ffa..8a2305d0ef 100644 --- a/packages/components-angular/projects/components/CHANGELOG.md +++ b/packages/components-angular/projects/components/CHANGELOG.md @@ -1,5 +1,12 @@ # @swisspost/design-system-components-angular +## 9.0.0-next.4 + +### Patch Changes + +- Updated dependencies: + - @swisspost/design-system-components@9.0.0-next.4 + ## 9.0.0-next.3 ### Minor Changes diff --git a/packages/components-angular/projects/components/package.json b/packages/components-angular/projects/components/package.json index e82f5da166..040548d639 100644 --- a/packages/components-angular/projects/components/package.json +++ b/packages/components-angular/projects/components/package.json @@ -1,6 +1,6 @@ { "name": "@swisspost/design-system-components-angular", - "version": "9.0.0-next.3", + "version": "9.0.0-next.4", "description": "Swiss Post Design System - Angular Wrapper Components", "author": "Swiss Post ", "license": "Apache-2.0", @@ -19,7 +19,7 @@ }, "dependencies": { "tslib": "2.6.3", - "@swisspost/design-system-components": "workspace:9.0.0-next.3" + "@swisspost/design-system-components": "workspace:9.0.0-next.4" }, "peerDependencies": { "@angular/common": "^16.0.0 || ^17.0.0 || ^18.0.0", diff --git a/packages/components-react/CHANGELOG.md b/packages/components-react/CHANGELOG.md index 2345d27a43..e1503d21ca 100644 --- a/packages/components-react/CHANGELOG.md +++ b/packages/components-react/CHANGELOG.md @@ -1,5 +1,12 @@ # @swisspost/design-system-components-react +## 9.0.0-next.4 + +### Patch Changes + +- Updated dependencies: + - @swisspost/design-system-components@9.0.0-next.4 + ## 9.0.0-next.3 ### Patch Changes diff --git a/packages/components-react/package.json b/packages/components-react/package.json index 8238f83559..76bb2f2ae5 100644 --- a/packages/components-react/package.json +++ b/packages/components-react/package.json @@ -1,6 +1,6 @@ { "name": "@swisspost/design-system-components-react", - "version": "9.0.0-next.3", + "version": "9.0.0-next.4", "description": "Design System React Components for easy integration with the React ecosystem", "author": "Swiss Post ", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "lint": "eslint src/**/*.ts" }, "dependencies": { - "@swisspost/design-system-components": "workspace:9.0.0-next.3" + "@swisspost/design-system-components": "workspace:9.0.0-next.4" }, "devDependencies": { "@types/node": "20.14.14", diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 4eb3171fc0..2f4eb8c9fb 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,5 +1,23 @@ # @swisspost/design-system-components +## 9.0.0-next.4 + +### Major Changes + +- Removed the `.breadcrumb-item` class, which previously handled styling for breadcrumb items. Introduced a new `post-breadcrumb-item` that should be used in place of the `.breadcrumb-item` class. (by [@alionazherdetska](https://github.com/alionazherdetska) with [#3659](https://github.com/swisspost/design-system/pull/3659)) + +### Minor Changes + +- Created the `post-list` and `post-list-item` components. (by [@myrta2302](https://github.com/myrta2302) with [#3812](https://github.com/swisspost/design-system/pull/3812)) + +- Added close button web component. (by [@leagrdv](https://github.com/leagrdv) with [#3880](https://github.com/swisspost/design-system/pull/3880)) + +### Patch Changes + +- Fixed an issue with the post-collapsible throwing an invalid selector error. (by [@alizedebray](https://github.com/alizedebray) with [#3726](https://github.com/swisspost/design-system/pull/3726)) +- Updated dependencies: + - @swisspost/design-system-styles@9.0.0-next.4 + ## 9.0.0-next.3 ### Major Changes diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 2355ec2939..2975071b22 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -113,6 +113,10 @@ By setting a component's name as a `component` property in your stories' metadat All components should have automated tests. These tests are available in the `cypress/e2e` folder. +### Set inital component Visibility to hidden + +To prevent flickering while components load, add each new component to the list in `packages/styles/src/utilities/_not-defined.scss`, which applies `visibility: hidden` to all listed components until they are registered by the browser (when the hydrated attribute actually starts to take effect). + ## Stencil ![Built With Stencil](https://img.shields.io/badge/-Built%20With%20Stencil-16161d.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjIuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI%2BCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI%2BCgkuc3Qwe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU%2BCjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik00MjQuNywzNzMuOWMwLDM3LjYtNTUuMSw2OC42LTkyLjcsNjguNkgxODAuNGMtMzcuOSwwLTkyLjctMzAuNy05Mi43LTY4LjZ2LTMuNmgzMzYuOVYzNzMuOXoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTQyNC43LDI5Mi4xSDE4MC40Yy0zNy42LDAtOTIuNy0zMS05Mi43LTY4LjZ2LTMuNkgzMzJjMzcuNiwwLDkyLjcsMzEsOTIuNyw2OC42VjI5Mi4xeiIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNNDI0LjcsMTQxLjdIODcuN3YtMy42YzAtMzcuNiw1NC44LTY4LjYsOTIuNy02OC42SDMzMmMzNy45LDAsOTIuNywzMC43LDkyLjcsNjguNlYxNDEuN3oiLz4KPC9zdmc%2BCg%3D%3D&colorA=16161d&style=flat-square) diff --git a/packages/components/cypress/e2e/closebutton.cy.ts b/packages/components/cypress/e2e/closebutton.cy.ts new file mode 100644 index 0000000000..275adfb50a --- /dev/null +++ b/packages/components/cypress/e2e/closebutton.cy.ts @@ -0,0 +1,22 @@ +const CLOSE_BTN_ID = 'de313349-0c0b-4baf-adc6-cb8c2e36fc1a'; + +describe('Close button', () => { + describe('default', () => { + beforeEach(() => { + cy.getComponent('post-closebutton', CLOSE_BTN_ID); + }); + + it('should render with a close button and a11y label', () => { + cy.get('@closebutton').should('exist'); + cy.get('@closebutton').find('.btn-icon-close').should('exist'); + cy.get('@closebutton').find('span.visually-hidden').should('exist'); + }); + }); +}); + +describe('Accessibility', () => { + it('Has no detectable a11y violations on load for all variants', () => { + cy.getSnapshots('post-closebutton'); + cy.checkA11y('#root-inner'); + }); +}); diff --git a/packages/components/package.json b/packages/components/package.json index f2591ed525..253f6811f7 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@swisspost/design-system-components", - "version": "9.0.0-next.3", + "version": "9.0.0-next.4", "description": "A collection of web components built with Stencil JS for the Swiss Post Design System.", "license": "Apache-2.0", "main": "dist/index.cjs.js", @@ -40,7 +40,7 @@ "dependencies": { "@floating-ui/dom": "1.6.8", "@oddbird/popover-polyfill": "0.3.7", - "@swisspost/design-system-styles": "workspace:9.0.0-next.3", + "@swisspost/design-system-styles": "workspace:9.0.0-next.4", "ally.js": "1.4.1", "long-press-event": "2.5.0" }, @@ -65,6 +65,7 @@ "rimraf": "6.0.1", "rollup-plugin-postcss": "4.0.2", "sass": "1.78.0", + "throttle-debounce": "5.0.2", "ts-jest": "29.2.4", "typescript": "5.5.4" }, diff --git a/packages/components/src/components.d.ts b/packages/components/src/components.d.ts index 5fb886f66a..46ee23749c 100644 --- a/packages/components/src/components.d.ts +++ b/packages/components/src/components.d.ts @@ -148,6 +148,8 @@ export namespace Components { */ "value": string; } + interface PostClosebutton { + } interface PostCollapsible { /** * If `true`, the element is collapsed otherwise it is displayed. @@ -168,6 +170,8 @@ export namespace Components { */ "update": () => Promise; } + interface PostHeader { + } /** * @class PostIcon - representing a stencil component */ @@ -241,6 +245,57 @@ export namespace Components { */ "url": string | URL; } + interface PostMainnavigation { + } + interface PostMegadropdown { + /** + * Hide megadropdown + * @returns boolean + */ + "hide": () => Promise; + /** + * Show megadropdown + * @param element HTMLElement + * @returns boolean + */ + "show": (element: HTMLElement) => Promise; + /** + * Toggle megadropdown + * @param element HTMLElement + * @param force boolean + * @returns boolean + */ + "toggle": (element: HTMLElement, force?: boolean) => Promise; + } + interface PostMegadropdownTrigger { + } + interface PostMenu { + /** + * Hides the popover menu and restores focus to the previously focused element. + */ + "hide": () => Promise; + /** + * 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. + */ + "placement"?: Placement; + /** + * Displays the popover menu, focusing the first menu item. + * @param target - The HTML element relative to which the popover menu should be displayed. + */ + "show": (target: HTMLElement) => Promise; + /** + * Toggles the menu visibility based on its current state. + */ + "toggle": (target: HTMLElement) => Promise; + } + interface PostMenuItem { + } + interface PostMenuTrigger { + /** + * ID of the menu element that this trigger is linked to. Used to open and close the specified menu. + */ + "for": string; + } interface PostPopover { /** * Show a little indicator arrow @@ -400,6 +455,18 @@ export interface PostLanguageOptionCustomEvent extends CustomEvent { detail: T; target: HTMLPostLanguageOptionElement; } +export interface PostMainnavigationCustomEvent extends CustomEvent { + detail: T; + target: HTMLPostMainnavigationElement; +} +export interface PostMegadropdownTriggerCustomEvent extends CustomEvent { + detail: T; + target: HTMLPostMegadropdownTriggerElement; +} +export interface PostMenuCustomEvent extends CustomEvent { + detail: T; + target: HTMLPostMenuElement; +} export interface PostPopovercontainerCustomEvent extends CustomEvent { detail: T; target: HTMLPostPopovercontainerElement; @@ -475,6 +542,12 @@ declare global { prototype: HTMLPostCardControlElement; new (): HTMLPostCardControlElement; }; + interface HTMLPostClosebuttonElement extends Components.PostClosebutton, HTMLStencilElement { + } + var HTMLPostClosebuttonElement: { + prototype: HTMLPostClosebuttonElement; + new (): HTMLPostClosebuttonElement; + }; interface HTMLPostCollapsibleElementEventMap { "postToggle": boolean; } @@ -498,6 +571,12 @@ declare global { prototype: HTMLPostCollapsibleTriggerElement; new (): HTMLPostCollapsibleTriggerElement; }; + interface HTMLPostHeaderElement extends Components.PostHeader, HTMLStencilElement { + } + var HTMLPostHeaderElement: { + prototype: HTMLPostHeaderElement; + new (): HTMLPostHeaderElement; + }; /** * @class PostIcon - representing a stencil component */ @@ -542,6 +621,75 @@ declare global { prototype: HTMLPostLogoElement; new (): HTMLPostLogoElement; }; + interface HTMLPostMainnavigationElementEventMap { + "postToggle": any; + } + interface HTMLPostMainnavigationElement extends Components.PostMainnavigation, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLPostMainnavigationElement, ev: PostMainnavigationCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLPostMainnavigationElement, ev: PostMainnavigationCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLPostMainnavigationElement: { + prototype: HTMLPostMainnavigationElement; + new (): HTMLPostMainnavigationElement; + }; + interface HTMLPostMegadropdownElement extends Components.PostMegadropdown, HTMLStencilElement { + } + var HTMLPostMegadropdownElement: { + prototype: HTMLPostMegadropdownElement; + new (): HTMLPostMegadropdownElement; + }; + interface HTMLPostMegadropdownTriggerElementEventMap { + "postToggle": any; + } + interface HTMLPostMegadropdownTriggerElement extends Components.PostMegadropdownTrigger, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLPostMegadropdownTriggerElement, ev: PostMegadropdownTriggerCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLPostMegadropdownTriggerElement, ev: PostMegadropdownTriggerCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLPostMegadropdownTriggerElement: { + prototype: HTMLPostMegadropdownTriggerElement; + new (): HTMLPostMegadropdownTriggerElement; + }; + interface HTMLPostMenuElementEventMap { + "toggleMenu": boolean; + } + interface HTMLPostMenuElement extends Components.PostMenu, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLPostMenuElement, ev: PostMenuCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLPostMenuElement, ev: PostMenuCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLPostMenuElement: { + prototype: HTMLPostMenuElement; + new (): HTMLPostMenuElement; + }; + interface HTMLPostMenuItemElement extends Components.PostMenuItem, HTMLStencilElement { + } + var HTMLPostMenuItemElement: { + prototype: HTMLPostMenuItemElement; + new (): HTMLPostMenuItemElement; + }; + interface HTMLPostMenuTriggerElement extends Components.PostMenuTrigger, HTMLStencilElement { + } + var HTMLPostMenuTriggerElement: { + prototype: HTMLPostMenuTriggerElement; + new (): HTMLPostMenuTriggerElement; + }; interface HTMLPostPopoverElement extends Components.PostPopover, HTMLStencilElement { } var HTMLPostPopoverElement: { @@ -631,13 +779,21 @@ declare global { "post-avatar": HTMLPostAvatarElement; "post-breadcrumb-item": HTMLPostBreadcrumbItemElement; "post-card-control": HTMLPostCardControlElement; + "post-closebutton": HTMLPostClosebuttonElement; "post-collapsible": HTMLPostCollapsibleElement; "post-collapsible-trigger": HTMLPostCollapsibleTriggerElement; + "post-header": HTMLPostHeaderElement; "post-icon": HTMLPostIconElement; "post-language-option": HTMLPostLanguageOptionElement; "post-list": HTMLPostListElement; "post-list-item": HTMLPostListItemElement; "post-logo": HTMLPostLogoElement; + "post-mainnavigation": HTMLPostMainnavigationElement; + "post-megadropdown": HTMLPostMegadropdownElement; + "post-megadropdown-trigger": HTMLPostMegadropdownTriggerElement; + "post-menu": HTMLPostMenuElement; + "post-menu-item": HTMLPostMenuItemElement; + "post-menu-trigger": HTMLPostMenuTriggerElement; "post-popover": HTMLPostPopoverElement; "post-popovercontainer": HTMLPostPopovercontainerElement; "post-rating": HTMLPostRatingElement; @@ -769,6 +925,8 @@ declare namespace LocalJSX { */ "value"?: string; } + interface PostClosebutton { + } interface PostCollapsible { /** * If `true`, the element is collapsed otherwise it is displayed. @@ -785,6 +943,8 @@ declare namespace LocalJSX { */ "for"?: string; } + interface PostHeader { + } /** * @class PostIcon - representing a stencil component */ @@ -858,6 +1018,38 @@ declare namespace LocalJSX { */ "url"?: string | URL; } + interface PostMainnavigation { + /** + * Gets emitted when a user closes the main navigation on mobile + */ + "onPostToggle"?: (event: PostMainnavigationCustomEvent) => void; + } + interface PostMegadropdown { + } + interface PostMegadropdownTrigger { + /** + * Emits after each toggle + */ + "onPostToggle"?: (event: PostMegadropdownTriggerCustomEvent) => void; + } + interface PostMenu { + /** + * Emits when the menu is shown or hidden. The event payload is a boolean: `true` when the menu was opened, `false` when it was closed. + */ + "onToggleMenu"?: (event: PostMenuCustomEvent) => void; + /** + * 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. + */ + "placement"?: Placement; + } + interface PostMenuItem { + } + interface PostMenuTrigger { + /** + * ID of the menu element that this trigger is linked to. Used to open and close the specified menu. + */ + "for": string; + } interface PostPopover { /** * Show a little indicator arrow @@ -973,13 +1165,21 @@ declare namespace LocalJSX { "post-avatar": PostAvatar; "post-breadcrumb-item": PostBreadcrumbItem; "post-card-control": PostCardControl; + "post-closebutton": PostClosebutton; "post-collapsible": PostCollapsible; "post-collapsible-trigger": PostCollapsibleTrigger; + "post-header": PostHeader; "post-icon": PostIcon; "post-language-option": PostLanguageOption; "post-list": PostList; "post-list-item": PostListItem; "post-logo": PostLogo; + "post-mainnavigation": PostMainnavigation; + "post-megadropdown": PostMegadropdown; + "post-megadropdown-trigger": PostMegadropdownTrigger; + "post-menu": PostMenu; + "post-menu-item": PostMenuItem; + "post-menu-trigger": PostMenuTrigger; "post-popover": PostPopover; "post-popovercontainer": PostPopovercontainer; "post-rating": PostRating; @@ -1003,8 +1203,10 @@ declare module "@stencil/core" { * @class PostCardControl - representing a stencil component */ "post-card-control": LocalJSX.PostCardControl & JSXBase.HTMLAttributes; + "post-closebutton": LocalJSX.PostClosebutton & JSXBase.HTMLAttributes; "post-collapsible": LocalJSX.PostCollapsible & JSXBase.HTMLAttributes; "post-collapsible-trigger": LocalJSX.PostCollapsibleTrigger & JSXBase.HTMLAttributes; + "post-header": LocalJSX.PostHeader & JSXBase.HTMLAttributes; /** * @class PostIcon - representing a stencil component */ @@ -1013,6 +1215,12 @@ declare module "@stencil/core" { "post-list": LocalJSX.PostList & JSXBase.HTMLAttributes; "post-list-item": LocalJSX.PostListItem & JSXBase.HTMLAttributes; "post-logo": LocalJSX.PostLogo & JSXBase.HTMLAttributes; + "post-mainnavigation": LocalJSX.PostMainnavigation & JSXBase.HTMLAttributes; + "post-megadropdown": LocalJSX.PostMegadropdown & JSXBase.HTMLAttributes; + "post-megadropdown-trigger": LocalJSX.PostMegadropdownTrigger & JSXBase.HTMLAttributes; + "post-menu": LocalJSX.PostMenu & JSXBase.HTMLAttributes; + "post-menu-item": LocalJSX.PostMenuItem & JSXBase.HTMLAttributes; + "post-menu-trigger": LocalJSX.PostMenuTrigger & JSXBase.HTMLAttributes; "post-popover": LocalJSX.PostPopover & JSXBase.HTMLAttributes; "post-popovercontainer": LocalJSX.PostPopovercontainer & JSXBase.HTMLAttributes; "post-rating": LocalJSX.PostRating & JSXBase.HTMLAttributes; diff --git a/packages/components/src/components/post-closebutton/post-closebutton.tsx b/packages/components/src/components/post-closebutton/post-closebutton.tsx new file mode 100644 index 0000000000..68d13ef51a --- /dev/null +++ b/packages/components/src/components/post-closebutton/post-closebutton.tsx @@ -0,0 +1,26 @@ +import { Component, Element, h, Host } from '@stencil/core'; +import { version } from '@root/package.json'; + +/** + * @slot default - Slot for placing visually hidden label in the close button. + */ +@Component({ + tag: 'post-closebutton', + shadow: false, +}) +export class PostClosebutton { + @Element() host: HTMLPostClosebuttonElement; + + render() { + return ( + + + + ); + } +} diff --git a/packages/components/src/components/post-closebutton/readme.md b/packages/components/src/components/post-closebutton/readme.md new file mode 100644 index 0000000000..ca0053c6df --- /dev/null +++ b/packages/components/src/components/post-closebutton/readme.md @@ -0,0 +1,30 @@ +# post-closebutton + + + + + + +## Slots + +| Slot | Description | +| ----------- | ----------------------------------------------------------- | +| `"default"` | Slot for placing visually hidden label in the close button. | + + +## Dependencies + +### Depends on + +- [post-icon](../post-icon) + +### Graph +```mermaid +graph TD; + post-closebutton --> post-icon + style post-closebutton fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/components/src/components/post-header/post-header.scss b/packages/components/src/components/post-header/post-header.scss new file mode 100644 index 0000000000..9703e1e3ea --- /dev/null +++ b/packages/components/src/components/post-header/post-header.scss @@ -0,0 +1,136 @@ +@use '@swisspost/design-system-styles/mixins/media'; + +*, +::before, +::after { + box-sizing: border-box; +} + +:host { + --global-header-height: 72px; + --main-header-height: 56px; + --header-height: calc(var(--global-header-height) + var(--main-header-height)); + + @include media.max(lg) { + --global-header-height: 64px; + } +} + +.d-flex { + display: flex; +} + +.space-between { + justify-content: space-between; +} + +.global-header { + background-color: #ffcc00; + display: flex; + justify-content: space-between; + align-items: center; + position: sticky; + padding-inline-start: 4px; + padding-inline-end: 12px; + + height: var(--global-header-height); + + @include media.max(lg) { + top: 0; + } + + @include media.min(lg) { + top: calc((var(--global-header-height) - 24px) * -1); + } +} + +slot[name='post-logo'] { + align-self: flex-end; +} + +.global-sub { + display: flex; + align-items: center; + gap: 2rem; + height: var(--global-header-height); +} + +.align-end { + align-items: flex-end; +} + +.logo { + flex: 1 0 auto; + height: var(--global-header-height); + width: var(--global-header-height); + min-height: 24px; + align-self: flex-end; + + @include media.min(lg) { + height: calc(var(--global-header-height) - var(--header-scroll-top)); + } +} + +::slotted(ul) { + margin-block: 0; + list-style: none; + display: flex; + padding-left: 0; + gap: 1rem; +} + +.title-header, +.main-navigation { + display: flex; + align-items: center; + padding-inline: 12px; + background: white; +} + +.title-header { + height: var(--main-header-height); + display: flex; + align-items: center; + + @include media.max(lg) { + border-bottom: 1px solid black; + } +} +:host(:not(:has([slot='title']))) .title-header { + display: none; +} + +::slotted(h1) { + margin: 0 !important; + font-size: 28px !important; +} + +.main-navigation { + position: sticky; + top: 24px; + height: var(--main-header-height); + + @include media.min(lg) { + border-bottom: 1px solid black; + } + + @include media.max(lg) { + display: none; + position: absolute; + top: var(--header-height); + bottom: 0; + width: 100%; + background-color: white; + height: auto; + + &.extended { + display: block; + } + } +} + +.mobile-toggle { + @include media.min(lg) { + display: none; + } +} diff --git a/packages/components/src/components/post-header/post-header.tsx b/packages/components/src/components/post-header/post-header.tsx new file mode 100644 index 0000000000..0461f5d135 --- /dev/null +++ b/packages/components/src/components/post-header/post-header.tsx @@ -0,0 +1,133 @@ +import { Component, h, Host, State, Element, Listen } from '@stencil/core'; +import { throttle } from 'throttle-debounce'; +import { version } from '@root/package.json'; + +@Component({ + tag: 'post-header', + shadow: true, + styleUrl: './post-header.scss', +}) +export class PostHeader { + @Element() host: HTMLPostHeaderElement; + @State() device: 'mobile' | 'tablet' | 'desktop' = null; + @State() mobileMenuExtended: boolean = false; + + private scrollParent = null; + private throttledScroll = () => this.handleScrollEvent(); + private throttledResize = throttle(50, () => this.handleResize()); + + componentWillRender() { + this.scrollParent = this.getScrollParent(this.host); + this.scrollParent.addEventListener('scroll', this.throttledScroll, { passive: true }); + window.addEventListener('resize', this.throttledResize, { passive: true }); + this.handleResize(); + this.handleScrollEvent(); + } + + @Listen('postMainNavigationClosed') + handlePostMainNavigationClosed() { + this.mobileMenuExtended = false; + } + + private handleScrollEvent() { + // Credits: "https://github.com/qeremy/so/blob/master/so.dom.js#L426" + const st = Math.max( + 0, + this.scrollParent instanceof Document + ? this.scrollParent.documentElement.scrollTop + : this.scrollParent.scrollTop, + ); + + this.host.style.setProperty('--header-scroll-top', `${st}px`); + } + + private getScrollParent(node: Element): Element | Document { + let currentParent = node.parentElement; + while (currentParent) { + if (currentParent.nodeName === 'BODY') { + return document; + } + if (this.isScrollable(currentParent)) { + return currentParent; + } + currentParent = currentParent.parentElement; + } + return document; + } + + private isScrollable(node: Element) { + if (!(node instanceof HTMLElement || node instanceof SVGElement)) { + return false; + } + const style = getComputedStyle(node); + return ['overflow', 'overflow-x', 'overflow-y'].some(propertyName => { + const value = style.getPropertyValue(propertyName); + return value === 'auto' || value === 'scroll'; + }); + } + + private handleResize() { + const width = window?.innerWidth; + if (width >= 1024) { + this.device = 'desktop'; + this.mobileMenuExtended = false; // Close any open mobile menu + } else if (width >= 600) { + this.device = 'tablet'; + } else { + this.device = 'mobile'; + } + } + + private handleMobileMenuToggle() { + this.mobileMenuExtended = !this.mobileMenuExtended; + } + + render() { + const mainNavClasses = ['main-navigation']; + if (this.mobileMenuExtended) { + mainNavClasses.push('extended'); + } + + return ( + + + +
+ +
+ + +
+
+ +
+ {(this.device === 'mobile' || this.device === 'tablet') && ( + + )} + + {(this.device === 'mobile' || this.device === 'tablet') && ( + + )} + {(this.device === 'mobile' || this.device === 'tablet') && ( + + )} +
+
+ ); + } +} diff --git a/packages/components/src/components/post-header/readme.md b/packages/components/src/components/post-header/readme.md new file mode 100644 index 0000000000..0605234dfe --- /dev/null +++ b/packages/components/src/components/post-header/readme.md @@ -0,0 +1,10 @@ +# post-header + + + + + + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/components/src/components/post-icon/readme.md b/packages/components/src/components/post-icon/readme.md index 92d1a4bacb..afb94ab2e7 100644 --- a/packages/components/src/components/post-icon/readme.md +++ b/packages/components/src/components/post-icon/readme.md @@ -25,6 +25,7 @@ some content - [post-alert](../post-alert) - [post-breadcrumb-item](../post-breadcrumb-item) - [post-card-control](../post-card-control) + - [post-closebutton](../post-closebutton) - [post-rating](../post-rating) - [post-tag](../post-tag) @@ -34,6 +35,7 @@ graph TD; post-alert --> post-icon post-breadcrumb-item --> post-icon post-card-control --> post-icon + post-closebutton --> post-icon post-rating --> post-icon post-tag --> post-icon style post-icon fill:#f9f,stroke:#333,stroke-width:4px diff --git a/packages/components/src/components/post-logo/post-logo.scss b/packages/components/src/components/post-logo/post-logo.scss index e03e4a6e88..ef7dfcf04e 100644 --- a/packages/components/src/components/post-logo/post-logo.scss +++ b/packages/components/src/components/post-logo/post-logo.scss @@ -14,4 +14,3 @@ .description { @include utilities.visuallyhidden; } - diff --git a/packages/components/src/components/post-logo/post-logo.tsx b/packages/components/src/components/post-logo/post-logo.tsx index d5ba21c1da..313cb0389d 100644 --- a/packages/components/src/components/post-logo/post-logo.tsx +++ b/packages/components/src/components/post-logo/post-logo.tsx @@ -41,7 +41,7 @@ export class PostLogo { const LogoTag = logoLink ? 'a' : 'span'; return ( - +