From 97596917eecb1c68363308d651bd6e61df353a77 Mon Sep 17 00:00:00 2001 From: Alona Zherdetska <138328641+alionazherdetska@users.noreply.github.com> Date: Thu, 12 Dec 2024 09:20:37 +0100 Subject: [PATCH 01/13] fix(workflow): move create preview comment job to deploy documentation workflow (#4207) --- .github/actions/preview/message/create/action.yaml | 10 ++++++++-- .github/workflows/build-documentation.yaml | 5 ----- .github/workflows/deploy-documentation.yaml | 6 ++++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/actions/preview/message/create/action.yaml b/.github/actions/preview/message/create/action.yaml index 3d56790012..61d2e82b8c 100644 --- a/.github/actions/preview/message/create/action.yaml +++ b/.github/actions/preview/message/create/action.yaml @@ -5,14 +5,20 @@ inputs: access-token: description: The access token to use for commenting. required: true + issue-number: + description: The issue number from the caller workflow. + required: true runs: using: composite steps: - uses: actions/github-script@v7 + env: + ISSUE_NUMBER: ${{ inputs.issue-number }} with: github-token: ${{ inputs.access-token }} script: | + const { ISSUE_NUMBER } = process.env const commentTitle = '**Related Previews**' const commentInitialBody = 'Preview URLs will be added here, once they are ready... ![loader](https://github.com/swisspost/design-system/assets/9716662/49a75898-7093-4ffb-9460-071ff194459d)' @@ -25,7 +31,7 @@ runs: await github.rest.issues.createComment({ repo: context.repo.repo, owner: context.repo.owner, - issue_number: context.issue.number, + issue_number: ISSUE_NUMBER, body: `${commentTitle}\n${commentInitialBody}` }) @@ -45,7 +51,7 @@ runs: comments = (await github.rest.issues.listComments({ repo: context.repo.repo, owner: context.repo.owner, - issue_number: context.issue.number + issue_number: ISSUE_NUMBER })).data || [] previewComment = comments.find(c => c.user.login === 'swisspost-bot' && c.body.includes(commentTitle)) diff --git a/.github/workflows/build-documentation.yaml b/.github/workflows/build-documentation.yaml index 168fd01d45..99783a8e4a 100644 --- a/.github/workflows/build-documentation.yaml +++ b/.github/workflows/build-documentation.yaml @@ -25,11 +25,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Create preview message - uses: ./.github/actions/preview/message/create - with: - access-token: ${{ secrets.SWISSPOSTDEVS_ACCESS_TOKEN }} - - name: Setup uses: ./.github/actions/setup-pnpm diff --git a/.github/workflows/deploy-documentation.yaml b/.github/workflows/deploy-documentation.yaml index 1f4043aebc..7b54c52c35 100644 --- a/.github/workflows/deploy-documentation.yaml +++ b/.github/workflows/deploy-documentation.yaml @@ -26,6 +26,12 @@ jobs: name: design-system-documentation folder: build-output + - name: Create preview message + uses: ./.github/actions/preview/message/create + with: + access-token: ${{ secrets.SWISSPOSTDEVS_ACCESS_TOKEN }} + issue-number: ${{ steps.build.outputs.id }} + - name: Get netlify config id: netlify-config uses: actions/github-script@v7 From 2d8248ea48a0fe1f27860f38c669596adb24d4e6 Mon Sep 17 00:00:00 2001 From: Lea Date: Thu, 12 Dec 2024 16:18:48 +0100 Subject: [PATCH 02/13] chore(styles, documentation): update styles of btn-link (#4200) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Oliver Schürch --- .changeset/twenty-chairs-kick.md | 6 +++++ .../components/button/button.stories.ts | 5 ++-- .../web-component/card-control.stories.ts | 4 +++- .../form-footer/form-footer.stories.ts | 2 +- packages/styles/src/components/button.scss | 23 ++++++++++++++----- 5 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 .changeset/twenty-chairs-kick.md diff --git a/.changeset/twenty-chairs-kick.md b/.changeset/twenty-chairs-kick.md new file mode 100644 index 0000000000..751cef6c67 --- /dev/null +++ b/.changeset/twenty-chairs-kick.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-documentation': minor +'@swisspost/design-system-styles': minor +--- + +Updated `.btn-link` to look like a regular link and old `.btn-link` is now `.btn-tertiary .px-0`. diff --git a/packages/documentation/src/stories/components/button/button.stories.ts b/packages/documentation/src/stories/components/button/button.stories.ts index 70c266ea5f..f8d2ba788e 100644 --- a/packages/documentation/src/stories/components/button/button.stories.ts +++ b/packages/documentation/src/stories/components/button/button.stories.ts @@ -86,10 +86,11 @@ const meta: MetaComponent = { 'btn-primary': 'Primary', 'btn-secondary': 'Secondary', 'btn-tertiary': 'Tertiary', - 'btn-link': 'Link (no padding)', + 'btn-tertiary px-0': 'Tertiary (no padding)', + 'btn-link': 'Link', }, }, - options: ['btn-primary', 'btn-secondary', 'btn-tertiary', 'btn-link'], + options: ['btn-primary', 'btn-secondary', 'btn-tertiary', 'btn-tertiary px-0', 'btn-link'], table: { category: 'General', }, diff --git a/packages/documentation/src/stories/components/card-control/web-component/card-control.stories.ts b/packages/documentation/src/stories/components/card-control/web-component/card-control.stories.ts index a0d9cf6994..2114493b81 100644 --- a/packages/documentation/src/stories/components/card-control/web-component/card-control.stories.ts +++ b/packages/documentation/src/stories/components/card-control/web-component/card-control.stories.ts @@ -310,7 +310,9 @@ export const FormIntegration: Story = { ${args.radioValidity === 'false' ? invalidFeedback : nothing}
- +
`; diff --git a/packages/documentation/src/stories/components/form-footer/form-footer.stories.ts b/packages/documentation/src/stories/components/form-footer/form-footer.stories.ts index 4d358a5bd1..c7105939c0 100644 --- a/packages/documentation/src/stories/components/form-footer/form-footer.stories.ts +++ b/packages/documentation/src/stories/components/form-footer/form-footer.stories.ts @@ -134,7 +134,7 @@ export function render(args: Args) { ? html` ` : null} ${args.showTertiaryButton - ? html`` diff --git a/packages/styles/src/components/button.scss b/packages/styles/src/components/button.scss index d2560ce8d2..0eaed65339 100644 --- a/packages/styles/src/components/button.scss +++ b/packages/styles/src/components/button.scss @@ -19,6 +19,7 @@ @use './../placeholders/button' as button-ph; @use '../functions/tokens'; @use '../tokens/components'; +@use '../tokens/elements'; tokens.$default-map: components.$post-button; @@ -39,9 +40,13 @@ tokens.$default-map: components.$post-button; color: var(--post-gray-80); font-family: inherit; font-weight: tokens.get('button-label-font-weight'); - text-decoration: none !important; // For tags, !important for hover + text-decoration: none; white-space: nowrap; // Long content should never break in buttons + &:hover { + text-decoration: none; + } + @include button-mx.button-size(); &:disabled { @@ -88,8 +93,6 @@ tokens.$default-map: components.$post-button; } } -// Tertiary -.btn-link, .btn-tertiary { // Styles to improve accessibility in high contrast mode @include utilities.high-contrast-mode() { @@ -105,8 +108,16 @@ tokens.$default-map: components.$post-button; } } -// Tertiary with no padding (overrides the padding defined by the sizing classes above) .btn-link { - padding-inline-start: 0; - padding-inline-end: 0; + text-decoration: tokens.get('link-decoration', elements.$post-link); + border: 0 none; + border-radius: tokens.get('link-border-radius', elements.$post-link); + min-height: 0; + font-size: inherit; + font-weight: inherit; + padding: 0; + + &:hover { + color: tokens.get('link-hover-fg', elements.$post-link); + } } From 34f6b54e8f0c7c836d67c785f0aeda6cc6a4df19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Sch=C3=BCrch?= Date: Thu, 12 Dec 2024 16:51:37 +0100 Subject: [PATCH 03/13] revert: deletion of the deplay-demo.yaml file on main (this is needed here for older versions) (#4234) --- .github/workflows/deploy-demo.yaml | 53 ++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/deploy-demo.yaml diff --git a/.github/workflows/deploy-demo.yaml b/.github/workflows/deploy-demo.yaml new file mode 100644 index 0000000000..cd2bf08ee3 --- /dev/null +++ b/.github/workflows/deploy-demo.yaml @@ -0,0 +1,53 @@ +@@ -1,51 +0,0 @@ +### +# +# Not used for v9 anymore +# But since workflows run always from the `main` branch we need to keep it for older versions +# +### + +name: Deploy Demo App Preview to Netlify +on: + workflow_run: + workflows: ['Build Demo App'] + types: + - completed + +jobs: + deploy: + runs-on: ubuntu-latest + if: > + ${{ github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup + uses: ./.github/actions/setup-pnpm + + - name: Download build artifacts + uses: ./.github/actions/artifact-download + id: build + with: + name: design-system-demo + folder: build-output + + - name: Deploy demo app to netlify + uses: ./.github/actions/deploy-to-netlify + id: deploy + with: + id: ${{ steps.build.outputs.id }} + netlify_auth_token: ${{ secrets.NETLIFY_AUTH_TOKEN }} + netlify_site_id: ${{ secrets.NETLIFY_SITE_ID }} + netlify_site_url: swisspost-web-frontend.netlify.app + folder: ${{ steps.build.outputs.folder }} + package_name: '@swisspost/design-system-demo' + + - name: Update preview message + uses: ./.github/actions/preview/message/update + with: + access-token: ${{ secrets.SWISSPOSTDEVS_ACCESS_TOKEN }} + issue-number: ${{ steps.build.outputs.id }} + preview-url: ${{ steps.deploy.outputs.preview-url }} From 7eb8af8409dc90019eaa289f7eeda841a257bfef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Sch=C3=BCrch?= Date: Thu, 12 Dec 2024 17:04:12 +0100 Subject: [PATCH 04/13] fix: deploy-demo.yaml workflow file (#4240) --- .github/workflows/deploy-demo.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy-demo.yaml b/.github/workflows/deploy-demo.yaml index cd2bf08ee3..8dfd3eda6e 100644 --- a/.github/workflows/deploy-demo.yaml +++ b/.github/workflows/deploy-demo.yaml @@ -1,4 +1,3 @@ -@@ -1,51 +0,0 @@ ### # # Not used for v9 anymore From 2b3010e71f5da72961e65687f05f72286bfe1b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aliz=C3=A9=20Debray?= <33580481+alizedebray@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:05:22 +0100 Subject: [PATCH 05/13] feat(components): update the post-togglebutton (#4223) --- .changeset/smooth-bugs-explode.md | 6 + .../components/cypress/e2e/togglebutton.cy.ts | 151 +++++++++--------- .../post-togglebutton/post-togglebutton.scss | 12 ++ .../post-togglebutton/post-togglebutton.tsx | 28 ++-- .../components/post-togglebutton/readme.md | 7 +- .../togglebutton/togglebutton.docs.mdx | 14 +- .../togglebutton.snapshot.stories.ts | 2 +- .../togglebutton/togglebutton.stories.ts | 34 ++-- 8 files changed, 139 insertions(+), 115 deletions(-) create mode 100644 .changeset/smooth-bugs-explode.md diff --git a/.changeset/smooth-bugs-explode.md b/.changeset/smooth-bugs-explode.md new file mode 100644 index 0000000000..9a822cca5d --- /dev/null +++ b/.changeset/smooth-bugs-explode.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-components': major +'@swisspost/design-system-documentation': patch +--- + +Updated the `post-togglebutton` component to offer greater flexibility. You can now control the visibility of elements within the `post-togglebutton` using the `data-showwhen="toggled"` and `data-showwhen="untoggled"` attributes. Any content without a `data-showwhen` attribute will always be visible, regardless of the toggle state. diff --git a/packages/components/cypress/e2e/togglebutton.cy.ts b/packages/components/cypress/e2e/togglebutton.cy.ts index 3df5fa773f..cdf2ec0895 100644 --- a/packages/components/cypress/e2e/togglebutton.cy.ts +++ b/packages/components/cypress/e2e/togglebutton.cy.ts @@ -1,114 +1,113 @@ const TOGGLE_BUTTON_ID = '1a6f47c2-5e8a-45a0-b1c3-9f7e2b834c24'; describe('togglebutton', () => { - beforeEach(() => { - cy.visit('/iframe.html?id=snapshots--toggle-button'); - cy.get('post-togglebutton', { timeout: 30000 }).should('be.visible'); - }); + describe('default', () => { + beforeEach(() => { + cy.getComponent('togglebutton', TOGGLE_BUTTON_ID); + }); - describe('default behavior', () => { - it('should toggle state when clicked', () => { - cy.get('post-togglebutton') - .first() - .as('button') - .shadow() - .find('slot[name="untoggled"]') - .should('exist'); + it('should show expected content', () => { + cy.get('@togglebutton').find('[data-showwhen="toggled"]').should('be.hidden'); + cy.get('@togglebutton').find('[data-showwhen="untoggled"]').should('be.visible'); + }); - cy.get('@button').click(); + it('should have correct ARIA attributes', () => { + cy.get('@togglebutton') + .should('have.attr', 'role', 'button') + .and('have.attr', 'aria-pressed', 'false') + .and('have.attr', 'tabindex', '0'); + }); + + it('should toggle state when clicked', () => { + cy.get('@togglebutton').click(); - cy.get('@button').shadow().find('slot[name="toggled"]').should('exist'); + cy.get('@togglebutton').find('[data-showwhen="toggled"]').should('be.visible'); + cy.get('@togglebutton').find('[data-showwhen="untoggled"]').should('be.hidden'); + cy.get('@togglebutton').should('have.attr', 'aria-pressed', 'true'); }); it('should toggle state when pressing Enter key', () => { - cy.get('post-togglebutton') - .first() - .as('button') - .shadow() - .find('slot[name="untoggled"]') - .should('exist'); + cy.get('@togglebutton').trigger('keydown', { key: 'Enter' }); - cy.get('@button').trigger('keydown', { key: 'Enter' }); + cy.get('@togglebutton').find('[data-showwhen="toggled"]').should('be.visible'); + cy.get('@togglebutton').find('[data-showwhen="untoggled"]').should('be.hidden'); + cy.get('@togglebutton').should('have.attr', 'aria-pressed', 'true'); + }); + }); - cy.get('@button').shadow().find('slot[name="toggled"]').should('exist'); + describe('initially toggled', () => { + beforeEach(() => { + cy.getComponent('togglebutton', TOGGLE_BUTTON_ID, 'initially-toggled'); + }); + + it('should show expected content', () => { + cy.get('@togglebutton').find('[data-showwhen="toggled"]').should('be.visible'); + cy.get('@togglebutton').find('[data-showwhen="untoggled"]').should('be.hidden'); }); it('should have correct ARIA attributes', () => { - cy.get('post-togglebutton') - .first() - .as('button') + cy.get('@togglebutton') .should('have.attr', 'role', 'button') - .and('have.attr', 'aria-pressed', 'false') + .and('have.attr', 'aria-pressed', 'true') .and('have.attr', 'tabindex', '0'); - - cy.get('@button').click(); - - cy.get('@button').should('have.attr', 'aria-pressed', 'true'); }); - }); - describe('initial state', () => { - it('should respect initial toggled state', () => { - cy.get('post-togglebutton[toggled]') - .first() - .as('toggledButton') - .shadow() - .find('slot[name="toggled"]') - .should('exist'); + it('should toggle state when clicked', () => { + cy.get('@togglebutton').click(); - cy.get('@toggledButton').should('have.attr', 'aria-pressed', 'true'); + cy.get('@togglebutton').find('[data-showwhen="toggled"]').should('be.hidden'); + cy.get('@togglebutton').find('[data-showwhen="untoggled"]').should('be.visible'); + cy.get('@togglebutton').should('have.attr', 'aria-pressed', 'false'); }); - it('should respect untoggled state', () => { - cy.get('post-togglebutton:not([toggled])') - .first() - .as('untoggledButton') - .shadow() - .find('slot[name="untoggled"]') - .should('exist'); + it('should toggle state when pressing Enter key', () => { + cy.get('@togglebutton').trigger('keydown', { key: 'Enter' }); - cy.get('@untoggledButton').should('have.attr', 'aria-pressed', 'false'); + cy.get('@togglebutton').find('[data-showwhen="toggled"]').should('be.hidden'); + cy.get('@togglebutton').find('[data-showwhen="untoggled"]').should('be.visible'); + cy.get('@togglebutton').should('have.attr', 'aria-pressed', 'false'); }); }); - describe('slot content', () => { - it('should display correct slot content based on toggle state', () => { - cy.get('post-togglebutton').first().as('button'); + describe('content visibility', () => { + beforeEach(() => { + cy.getComponent('togglebutton', TOGGLE_BUTTON_ID, 'content-visibility'); + }); - cy.get('@button').shadow().find('slot[name="untoggled"]').should('exist'); + it('should display correct contents on initial state', () => { + cy.get('@togglebutton').find('[data-showwhen="toggled"]').should('be.hidden'); + cy.get('@togglebutton').find('[data-showwhen="untoggled"]').should('be.visible'); + cy.get('@togglebutton').find(':not([data-showwhen])').should('be.visible'); + }); - cy.get('@button').click(); + it('should display correct contents when clicked', () => { + cy.get('@togglebutton').click(); - cy.get('@button').shadow().find('slot[name="toggled"]').should('exist'); + cy.get('@togglebutton').find('[data-showwhen="toggled"]').should('be.visible'); + cy.get('@togglebutton').find('[data-showwhen="untoggled"]').should('be.hidden'); + cy.get('@togglebutton').find(':not([data-showwhen])').should('be.visible'); + }); - cy.get('@button').click(); + it('should display correct contents when clicked twice', () => { + cy.get('@togglebutton').dblclick(); - cy.get('@button').shadow().find('slot[name="untoggled"]').should('exist'); + cy.get('@togglebutton').find('[data-showwhen="toggled"]').should('be.hidden'); + cy.get('@togglebutton').find('[data-showwhen="untoggled"]').should('be.visible'); + cy.get('@togglebutton').find(':not([data-showwhen])').should('be.visible'); }); }); +}); - describe('version attribute', () => { - it('should have the correct version data attribute', () => { - cy.get('post-togglebutton').first().should('have.attr', 'data-version'); - }); +describe('Accessibility', () => { + beforeEach(() => { + cy.getSnapshots('togglebutton'); }); - describe('Accessibility', () => { - beforeEach(() => { - cy.injectAxe(); - }); - - it('Has no detectable a11y violations on load for all variants', () => { - cy.checkA11y('#root-inner'); - }); + it('Has no detectable a11y violations on load for all variants', () => { + cy.checkA11y('#root-inner'); + }); - it('Should be keyboard navigable', () => { - cy.get('post-togglebutton') - .first() - .focus() - .should('have.focus') - .trigger('keydown', { key: 'Enter' }) - .should('have.attr', 'aria-pressed', 'true'); - }); + it('Should be keyboard navigable', () => { + cy.get('post-togglebutton').first().focus().should('have.focus'); }); }); diff --git a/packages/components/src/components/post-togglebutton/post-togglebutton.scss b/packages/components/src/components/post-togglebutton/post-togglebutton.scss index 3657761313..b73f35a5b6 100644 --- a/packages/components/src/components/post-togglebutton/post-togglebutton.scss +++ b/packages/components/src/components/post-togglebutton/post-togglebutton.scss @@ -1,3 +1,15 @@ :host { cursor: pointer; } + +:host([aria-pressed="true"]) { + ::slotted([data-showwhen="untoggled"]) { + display: none; + } +} + +:host([aria-pressed="false"]) { + ::slotted([data-showwhen="toggled"]) { + display: none; + } +} diff --git a/packages/components/src/components/post-togglebutton/post-togglebutton.tsx b/packages/components/src/components/post-togglebutton/post-togglebutton.tsx index 0f5d884884..b34c4f8282 100644 --- a/packages/components/src/components/post-togglebutton/post-togglebutton.tsx +++ b/packages/components/src/components/post-togglebutton/post-togglebutton.tsx @@ -1,9 +1,9 @@ -import { Component, Host, h, Prop } from '@stencil/core'; +import { Component, Host, h, Prop, Watch } from '@stencil/core'; import { version } from '@root/package.json'; +import { checkType } from '@/utils'; /** - * @slot toggled - Slot for content displayed when the button is in the "on" state. - * @slot untoggled - Slot for content displayed when the button is in the "off" state. + * @slot default - Slot for the content of the button. */ @Component({ @@ -15,7 +15,20 @@ export class PostTogglebutton { /** * If `true`, the button is in the "on" state, otherwise it is in the "off" state. */ - @Prop({ reflect: true, mutable: true }) toggled: boolean = false; + @Prop({ mutable: true }) toggled: boolean = false; + + @Watch('toggled') + validateToggled(value = this.toggled) { + checkType( + value, + 'boolean', + 'The "toggled" property of the post-togglebutton must be a boolean.', + ); + } + + componentWillLoad() { + this.validateToggled(); + } private handleClick = () => { this.toggled = !this.toggled; @@ -38,12 +51,7 @@ export class PostTogglebutton { onClick={this.handleClick} onKeyDown={this.handleKeydown} > - - + ); } diff --git a/packages/components/src/components/post-togglebutton/readme.md b/packages/components/src/components/post-togglebutton/readme.md index 7a5198c2dc..4d3c6be785 100644 --- a/packages/components/src/components/post-togglebutton/readme.md +++ b/packages/components/src/components/post-togglebutton/readme.md @@ -14,10 +14,9 @@ ## Slots -| Slot | Description | -| ------------- | ----------------------------------------------------------------- | -| `"toggled"` | Slot for content displayed when the button is in the "on" state. | -| `"untoggled"` | Slot for content displayed when the button is in the "off" state. | +| Slot | Description | +| ----------- | ----------------------------------- | +| `"default"` | Slot for the content of the button. | ---------------------------------------------- diff --git a/packages/documentation/src/stories/components/togglebutton/togglebutton.docs.mdx b/packages/documentation/src/stories/components/togglebutton/togglebutton.docs.mdx index 69c8efda8a..f531c17ebb 100644 --- a/packages/documentation/src/stories/components/togglebutton/togglebutton.docs.mdx +++ b/packages/documentation/src/stories/components/togglebutton/togglebutton.docs.mdx @@ -3,7 +3,7 @@ import * as toggleButtonStories from './togglebutton.stories'; -# Toggle Button +# Button Toggle

A toggle button component to switch between two states and display different content based on the current state.

@@ -24,8 +24,14 @@ You can set the button to be toggled initially by using the `toggled` attribute. -### Icon content +### Content Visibility -You can also add an icon to the content (for a toggle menu, for example). +The toggle button can contain any text or inline element. - +You can control the visibility of an element based on the state of the toggle button using the `data-showwhen` attribute. + +- If the element has a `data-showwhen="toggled"` attribute, it will be visible when the button is toggled on. +- If the element has a `data-showwhen="untoggled"` attribute, it will be visible when the button is toggled off. +- If the element **does not** have a `data-showwhen` attribute, it will always be visible, regardless of the button's state. + + diff --git a/packages/documentation/src/stories/components/togglebutton/togglebutton.snapshot.stories.ts b/packages/documentation/src/stories/components/togglebutton/togglebutton.snapshot.stories.ts index 00fa652e34..1ce0761562 100644 --- a/packages/documentation/src/stories/components/togglebutton/togglebutton.snapshot.stories.ts +++ b/packages/documentation/src/stories/components/togglebutton/togglebutton.snapshot.stories.ts @@ -15,7 +15,7 @@ const SIZES = ['', 'btn-sm', 'btn-lg']; type Story = StoryObj; -export const ToggleButton: Story = { +export const Togglebutton: Story = { render: () => { const TOGGLED = [false, true]; diff --git a/packages/documentation/src/stories/components/togglebutton/togglebutton.stories.ts b/packages/documentation/src/stories/components/togglebutton/togglebutton.stories.ts index 361f3b911a..c32c98918b 100644 --- a/packages/documentation/src/stories/components/togglebutton/togglebutton.stories.ts +++ b/packages/documentation/src/stories/components/togglebutton/togglebutton.stories.ts @@ -1,11 +1,9 @@ import { type Args, StoryObj } from '@storybook/web-components'; -import { html } from 'lit'; +import { html, nothing } from 'lit'; import { MetaComponent } from '@root/types'; -import { spread } from '@open-wc/lit-helpers'; import buttonMeta from '../button/button.stories'; export interface PostTogglebuttonProps { - type?: 'button' | 'submit' | 'reset'; toggled?: boolean; variant?: string; size?: string; @@ -15,7 +13,7 @@ export interface PostTogglebuttonProps { const meta: MetaComponent = { id: '1a6f47c2-5e8a-45a0-b1c3-9f7e2b834c24', - title: 'Components/Toggle Button', + title: 'Components/Button Toggle', tags: ['package:WebComponents'], render: renderBadge, component: 'post-togglebutton', @@ -70,34 +68,30 @@ const meta: MetaComponent = { export default meta; function renderBadge(args: Args) { + const btnClasses = ['btn', args.variant, args.size].filter(c => c && c !== 'null').join(' '); return html` - - ${args.contentWhenUntoggled} - ${args.contentWhenToggled} + + ${args.contentWhenUntoggled} + ${args.contentWhenToggled} `; } -function createProps(args: Args) { - return { - class: ['btn', args.variant, args.size].filter(c => c && c !== 'null').join(' '), - type: args.type, - ...(args.toggled && { toggled: true }), - }; -} - export const Default: StoryObj = {}; export const InitiallyToggled: StoryObj = { - render: args => html` ${renderBadge({ ...args, toggled: true })} `, + args: { + toggled: true, + }, }; -export const IconContent: StoryObj = { +export const ContentVisibility: StoryObj = { render: () => { return html` - - - + + Menu + + `; }, From e4e2997efc471f62f186e3640d7cfe5e19037d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aliz=C3=A9=20Debray?= <33580481+alizedebray@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:05:51 +0100 Subject: [PATCH 06/13] feat(components): update main navigation (#4210) --- packages/components/src/components.d.ts | 4 + .../components/post-header/post-header.scss | 10 +-- .../components/post-header/post-header.tsx | 19 +++-- .../src/components/post-header/readme.md | 13 +++ .../src/components/post-icon/readme.md | 2 - .../post-mainnavigation.scss | 84 +++++++++++++++---- .../post-mainnavigation.tsx | 33 ++++---- packages/components/src/index.html | 6 +- .../header/components/header.markup.ts | 6 +- 9 files changed, 121 insertions(+), 56 deletions(-) diff --git a/packages/components/src/components.d.ts b/packages/components/src/components.d.ts index 9acbc7fda6..fc3cb9961f 100644 --- a/packages/components/src/components.d.ts +++ b/packages/components/src/components.d.ts @@ -175,6 +175,10 @@ export namespace Components { "update": () => Promise; } interface PostHeader { + /** + * Toggles the mobile navigation. + */ + "toggleMobileMenu": () => Promise; } /** * @class PostIcon - representing a stencil component diff --git a/packages/components/src/components/post-header/post-header.scss b/packages/components/src/components/post-header/post-header.scss index 5879d615d4..8bea0377cf 100644 --- a/packages/components/src/components/post-header/post-header.scss +++ b/packages/components/src/components/post-header/post-header.scss @@ -25,19 +25,13 @@ justify-content: space-between; } -// ensures the global-header and title-header are above the main-navigation -.global-header, -.title-header { - position: relative; - z-index: 1; -} - .global-header { background-color: #ffcc00; display: flex; justify-content: space-between; align-items: center; position: sticky; + z-index: 2; padding-inline-start: 4px; padding-inline-end: 12px; @@ -88,6 +82,8 @@ slot[name='post-logo'] { } .title-header { + position: relative; + z-index: 1; display: flex; align-items: center; padding-inline: 12px; diff --git a/packages/components/src/components/post-header/post-header.tsx b/packages/components/src/components/post-header/post-header.tsx index c8a62dfbce..ac74a52518 100644 --- a/packages/components/src/components/post-header/post-header.tsx +++ b/packages/components/src/components/post-header/post-header.tsx @@ -1,4 +1,4 @@ -import { Component, h, Host, State, Element, Listen } from '@stencil/core'; +import { Component, h, Host, State, Element, Method } from '@stencil/core'; import { throttle } from 'throttle-debounce'; import { version } from '@root/package.json'; import { SwitchVariant } from '@/components'; @@ -27,9 +27,14 @@ export class PostHeader { this.handleScrollEvent(); } - @Listen('postMainNavigationClosed') - handlePostMainNavigationClosed() { - this.mobileMenuExtended = false; + /** + * Toggles the mobile navigation. + */ + @Method() + async toggleMobileMenu() { + if (this.device !== 'desktop') { + this.mobileMenuExtended = !this.mobileMenuExtended; + } } private handleScrollEvent() { @@ -97,10 +102,6 @@ export class PostHeader { this.host.querySelector('post-language-switch')?.setAttribute('variant', variant); } - private handleMobileMenuToggle() { - this.mobileMenuExtended = !this.mobileMenuExtended; - } - render() { const navigationClasses = ['navigation']; if (this.mobileMenuExtended) { @@ -119,7 +120,7 @@ export class PostHeader { {this.device === 'desktop' && } {this.device === 'desktop' && } -
this.handleMobileMenuToggle()} class="mobile-toggle"> +
this.toggleMobileMenu()} class="mobile-toggle">
diff --git a/packages/components/src/components/post-header/readme.md b/packages/components/src/components/post-header/readme.md index 0605234dfe..209d14f699 100644 --- a/packages/components/src/components/post-header/readme.md +++ b/packages/components/src/components/post-header/readme.md @@ -5,6 +5,19 @@ +## Methods + +### `toggleMobileMenu() => Promise` + +Toggles the mobile navigation. + +#### Returns + +Type: `Promise` + + + + ---------------------------------------------- *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 b5967432b0..5567a1b002 100644 --- a/packages/components/src/components/post-icon/readme.md +++ b/packages/components/src/components/post-icon/readme.md @@ -25,7 +25,6 @@ some content - [post-accordion-item](../post-accordion-item) - [post-back-to-top](../post-back-to-top) - [post-banner](../post-banner) - - [post-breadcrumb-item](../post-breadcrumb-item) - [post-card-control](../post-card-control) - [post-closebutton](../post-closebutton) - [post-language-switch](../post-language-switch) @@ -38,7 +37,6 @@ graph TD; post-accordion-item --> post-icon post-back-to-top --> post-icon post-banner --> post-icon - post-breadcrumb-item --> post-icon post-card-control --> post-icon post-closebutton --> post-icon post-language-switch --> post-icon diff --git a/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss b/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss index 7496c6c553..7ac5a14022 100644 --- a/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss +++ b/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss @@ -2,10 +2,13 @@ @use '@swisspost/design-system-styles/mixins/icons'; @use '@swisspost/design-system-styles/mixins/media'; @use '@swisspost/design-system-styles/mixins/utilities'; +@use '@swisspost/design-system-styles/functions/icons' as icon-fn; @use '@swisspost/design-system-styles/functions/tokens'; @use '@swisspost/design-system-styles/tokens/elements'; @use '@swisspost/design-system-styles/variables/animation'; +$nav-height: var(--post-core-dimension-56); + post-mainnavigation { // reset links and buttons post-list-item { @@ -21,28 +24,58 @@ post-mainnavigation { a, button { + display: flex; + align-items: center; + justify-content: space-between; + &:hover { color: tokens.get('post-link-hover-fg', elements.$post-link); } + + &:focus-visible { + border-radius: var(--post-core-dimension-4); + z-index: 1; + } } } // desktop styles @include media.min(lg) { - post-list > [role="list"] { - flex-direction: row; + nav { + position: relative; + max-width: 100vw; + max-height: $nav-height; + user-select: none; + } + + post-list { + margin-inline: var(--post-core-dimension-4); + + > [role="list"] { + flex-direction: row; + max-width: 100vw; + transition: transform animation.$transition-base-timing; + } } post-list-item { a, button { padding-inline: var(--post-core-dimension-12); - height: var(--post-core-dimension-56); - border-block: var(--post-core-dimension-4) solid transparent; - display: flex; - align-items: center; + height: $nav-height; gap: var(--post-core-dimension-4); font-size: var(--post-core-font-size-16); + border-block: 0 solid transparent; + border-block-end-color: currentColor; + + &:hover { + border-block-width: var(--post-core-dimension-2); + } + + &.selected { + border-block-width: var(--post-core-dimension-4); + font-weight: var(--post-core-font-weight-700); + } } // styles specific to for the mega-dropdown buttons @@ -58,29 +91,46 @@ post-mainnavigation { transition: transform animation.$transition-base-timing; } - &.selected { - border-block-end-color: currentColor; - font-weight: var(--post-core-font-weight-700); - - &::after { - transform: rotate(180deg); - } + &.selected::after { + transform: rotate(180deg); } } } + + [slot="back-button"] { + display: none; + } } // tablet/mobile styles @include media.max(lg) { + post-list > [role="list"] { + transform: none !important; + } + post-list-item { a, button { - display: block; width: 100%; - padding-block: var(--post-core-dimension-12); - padding-inline-end: var(--post-core-dimension-12); - border-bottom: var(--post-core-dimension-1) solid currentColor; + height: var(--post-core-dimension-48); + padding-inline-end: var(--post-core-dimension-6); + gap: var(--post-core-dimension-16); + border-block: var(--post-core-dimension-1) solid transparent; + border-block-end-color: currentColor; font-weight: var(--post-core-font-weight-700); + + &:hover, + &.selected { + border-block-width: var(--post-core-dimension-3); + } + + &:hover::after { + content: ''; + display: block; + @include icons.icon(3020); + width: var(--post-core-dimension-24); + height: var(--post-core-dimension-24); + } } } } diff --git a/packages/components/src/components/post-mainnavigation/post-mainnavigation.tsx b/packages/components/src/components/post-mainnavigation/post-mainnavigation.tsx index dc82772c1b..ad7b8c4557 100644 --- a/packages/components/src/components/post-mainnavigation/post-mainnavigation.tsx +++ b/packages/components/src/components/post-mainnavigation/post-mainnavigation.tsx @@ -1,4 +1,4 @@ -import { Component, Event, EventEmitter, Host, Listen, h } from '@stencil/core'; +import { Component, Event, EventEmitter, Host, h, Element } from '@stencil/core'; @Component({ tag: 'post-mainnavigation', @@ -6,28 +6,31 @@ import { Component, Event, EventEmitter, Host, Listen, h } from '@stencil/core'; styleUrl: './post-mainnavigation.scss', }) export class PostMainnavigation { + private header: HTMLPostHeaderElement | null; + + @Element() host: HTMLPostMainnavigationElement; + /** * Gets emitted when a user closes the main navigation on mobile */ @Event() postToggle: EventEmitter; - @Listen('postToggle') - handleMegadropdownToggled(event) { - // Find next element sibling - let megalodon; - let target = event.target; - while (target !== null) { - if (target.tagName === 'POST-MEGADROPDOWN') { - megalodon = target; - break; - } - target = target.nextElementSibling; - } - if (megalodon) megalodon.toggle(event.target); + /** + * Retrieves a reference to the closest 'post-header' element when the main navigation is added to the DOM. + */ + connectedCallback() { + this.header = this.host.closest('post-header'); + } + + /** + * Cleans up references and disconnects the MutationObserver when the main navigation is removed from the DOM. + */ + disconnectedCallback() { + this.header = null; } private handleBackButtonClick() { - this.postToggle.emit(); + if (this.header) this.header.toggleMobileMenu(); } render() { diff --git a/packages/components/src/index.html b/packages/components/src/index.html index 172bd137c5..dfa6592340 100644 --- a/packages/components/src/index.html +++ b/packages/components/src/index.html @@ -53,7 +53,7 @@

Application title

- @@ -68,7 +68,7 @@

Main Navigation

Briefe - +

Briefe title

Briefe senden

@@ -90,7 +90,7 @@

Schritt für Schritt

Pakete - +

Pakete title

Pakete senden

diff --git a/packages/documentation/src/stories/components/header/components/header.markup.ts b/packages/documentation/src/stories/components/header/components/header.markup.ts index f110177cf7..5c890e2916 100644 --- a/packages/documentation/src/stories/components/header/components/header.markup.ts +++ b/packages/documentation/src/stories/components/header/components/header.markup.ts @@ -38,7 +38,7 @@ export default html` - @@ -51,7 +51,7 @@ export default html` Briefe - +

Briefe title

Briefe senden

@@ -73,7 +73,7 @@ export default html` Pakete - +

Pakete title

Pakete senden

From 851388b5ddb88b353a1651aa9f92659220a8fce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Sch=C3=BCrch?= Date: Fri, 13 Dec 2024 08:04:07 +0100 Subject: [PATCH 07/13] fix: deploy-demo workflow use action from release/v8 branch (#4244) --- .github/workflows/deploy-demo.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-demo.yaml b/.github/workflows/deploy-demo.yaml index 8dfd3eda6e..df5a9f241b 100644 --- a/.github/workflows/deploy-demo.yaml +++ b/.github/workflows/deploy-demo.yaml @@ -34,7 +34,7 @@ jobs: folder: build-output - name: Deploy demo app to netlify - uses: ./.github/actions/deploy-to-netlify + uses: swisspost/design-system/.github/actions/deploy-to-netlify@release/v8 id: deploy with: id: ${{ steps.build.outputs.id }} From 140ba573702026e7fcfdec53e76bd5bc6e36f905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Sch=C3=BCrch?= Date: Fri, 13 Dec 2024 09:20:59 +0100 Subject: [PATCH 08/13] fix: deploy-demo workflow (#4247) --- .github/workflows/deploy-demo.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy-demo.yaml b/.github/workflows/deploy-demo.yaml index df5a9f241b..101553f1f7 100644 --- a/.github/workflows/deploy-demo.yaml +++ b/.github/workflows/deploy-demo.yaml @@ -22,6 +22,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_branch }} - name: Setup uses: ./.github/actions/setup-pnpm From 0368b848d8009105171bda93a26b70df6c9ba010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aliz=C3=A9=20Debray?= <33580481+alizedebray@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:31:29 +0100 Subject: [PATCH 09/13] chore(components): update slide animations (#4241) --- packages/components/src/animations/slide.ts | 8 ++++---- .../src/components/post-back-to-top/post-back-to-top.scss | 2 +- .../src/components/post-back-to-top/post-back-to-top.tsx | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/components/src/animations/slide.ts b/packages/components/src/animations/slide.ts index 2bd21d7cd4..7ab26f7392 100644 --- a/packages/components/src/animations/slide.ts +++ b/packages/components/src/animations/slide.ts @@ -2,10 +2,10 @@ const easing: string = 'ease'; const duration: number = 500; const fill: FillMode = 'forwards'; -export const slideUp = (el: HTMLElement, translateSize: string = '8rem'): Animation => { +export const slideUp = (el: HTMLElement, translateSize: string = '100%'): Animation => { return el.animate( [ - { transform: `translateY(-${translateSize})` }, // Starting position (no translation) + { transform: `translateY(${translateSize})` }, // Starting position (no translation) { transform: 'translateY(0)' }, // End position ], { @@ -16,11 +16,11 @@ export const slideUp = (el: HTMLElement, translateSize: string = '8rem'): Animat ); }; -export const slideDown = (el: HTMLElement, translateSize: string = '8rem'): Animation => { +export const slideDown = (el: HTMLElement, translateSize: string = '100%'): Animation => { return el.animate( [ { transform: 'translateY(0)' }, // Starting position (no translation) - { transform: `translateY(-${translateSize})` }, // End position + { transform: `translateY(${translateSize})` }, // End position ], { duration: duration, diff --git a/packages/components/src/components/post-back-to-top/post-back-to-top.scss b/packages/components/src/components/post-back-to-top/post-back-to-top.scss index 38cd94b925..1f9231ddde 100644 --- a/packages/components/src/components/post-back-to-top/post-back-to-top.scss +++ b/packages/components/src/components/post-back-to-top/post-back-to-top.scss @@ -8,7 +8,7 @@ tokens.$default-map: components.$post-floating-button; :host { - --post-floating-button-translate-y: #{tokens.get('post-floating-button-translate-y')}; + --post-floating-button-translate-y: calc(-1 * #{tokens.get('post-floating-button-translate-y')}); position: fixed; top: tokens.get('post-floating-button-position-top'); right: tokens.get('post-floating-button-position-right'); diff --git a/packages/components/src/components/post-back-to-top/post-back-to-top.tsx b/packages/components/src/components/post-back-to-top/post-back-to-top.tsx index f94dcf32a9..114de86b96 100644 --- a/packages/components/src/components/post-back-to-top/post-back-to-top.tsx +++ b/packages/components/src/components/post-back-to-top/post-back-to-top.tsx @@ -21,11 +21,11 @@ export class PostBackToTop { private translateY: string; - isBelowFold(): boolean { + private isBelowFold(): boolean { return window.scrollY > window.innerHeight; } - handleScroll = () => { + private handleScroll = () => { this.belowFold = this.isBelowFold(); }; @@ -39,7 +39,7 @@ export class PostBackToTop { } } - scrollToTop() { + private scrollToTop() { window.scrollTo({ top: 0, }); @@ -72,7 +72,7 @@ export class PostBackToTop { .getPropertyValue('--post-floating-button-translate-y'); if (!this.belowFold) { - this.el.style.transform = `translateY(-${this.translateY})`; + this.el.style.transform = `translateY(${this.translateY})`; } // Initial load From c14bede468f0fde24ee2f1f6a1fcd02dcdd0d5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aliz=C3=A9=20Debray?= <33580481+alizedebray@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:37:11 +0100 Subject: [PATCH 10/13] fix(components): fix post-togglebutton keyboard navigation (#4242) --- .changeset/nice-cycles-provide.md | 5 +++++ .../post-togglebutton/post-togglebutton.scss | 15 +++++++++++++++ .../post-togglebutton/post-togglebutton.tsx | 18 ++++++++++++------ 3 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 .changeset/nice-cycles-provide.md diff --git a/.changeset/nice-cycles-provide.md b/.changeset/nice-cycles-provide.md new file mode 100644 index 0000000000..a87368195c --- /dev/null +++ b/.changeset/nice-cycles-provide.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-components': patch +--- + +Updated the `post-togglebutton` to function like a real button, including support for keyboard navigation and proper focus styles. diff --git a/packages/components/src/components/post-togglebutton/post-togglebutton.scss b/packages/components/src/components/post-togglebutton/post-togglebutton.scss index b73f35a5b6..88ee2219af 100644 --- a/packages/components/src/components/post-togglebutton/post-togglebutton.scss +++ b/packages/components/src/components/post-togglebutton/post-togglebutton.scss @@ -1,5 +1,20 @@ +@use '@swisspost/design-system-styles/mixins/utilities'; +@use '@swisspost/design-system-styles/functions/tokens'; +@use '@swisspost/design-system-styles/tokens/helpers'; + :host { cursor: pointer; + outline-offset: tokens.get('focus-outline-offset', helpers.$post-focus) !important; + outline: tokens.get('focus-outline-color', helpers.$post-focus) none + tokens.get('focus-outline-width', helpers.$post-focus) !important; +} + +:host(:focus-visible) { + outline-style: tokens.get('focus-border-style', helpers.$post-focus) !important; + + @include utilities.high-contrast-mode() { + outline-color: Highlight !important; + } } :host([aria-pressed="true"]) { diff --git a/packages/components/src/components/post-togglebutton/post-togglebutton.tsx b/packages/components/src/components/post-togglebutton/post-togglebutton.tsx index b34c4f8282..b0eb329d9d 100644 --- a/packages/components/src/components/post-togglebutton/post-togglebutton.tsx +++ b/packages/components/src/components/post-togglebutton/post-togglebutton.tsx @@ -1,4 +1,4 @@ -import { Component, Host, h, Prop, Watch } from '@stencil/core'; +import { Component, Host, h, Prop, Watch, Element } from '@stencil/core'; import { version } from '@root/package.json'; import { checkType } from '@/utils'; @@ -12,6 +12,8 @@ import { checkType } from '@/utils'; shadow: true, }) export class PostTogglebutton { + @Element() host: HTMLPostTogglebuttonElement; + /** * If `true`, the button is in the "on" state, otherwise it is in the "off" state. */ @@ -28,6 +30,10 @@ export class PostTogglebutton { componentWillLoad() { this.validateToggled(); + + // add event listener to not override listener that might be set on the host + this.host.addEventListener('click', () => this.handleClick()); + this.host.addEventListener('keydown', (e: KeyboardEvent) => this.handleKeydown(e)); } private handleClick = () => { @@ -35,8 +41,10 @@ export class PostTogglebutton { }; private handleKeydown = (event: KeyboardEvent) => { - if (event.key === 'Enter') { - this.toggled = !this.toggled; + // perform a click when enter or spaced are pressed to mimic the button behavior + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); // prevents the page from scrolling when space is pressed + this.host.click(); } }; @@ -44,12 +52,10 @@ export class PostTogglebutton { return ( From fc67f691bb02c741a97a3476b386617a3b2528a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aliz=C3=A9=20Debray?= <33580481+alizedebray@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:42:16 +0100 Subject: [PATCH 11/13] feat(component): improve header styles and behavior (#4243) --- .../components/post-header/post-header.scss | 77 ++++++++++++------- .../components/post-header/post-header.tsx | 32 +++++--- .../post-language-switch.scss | 24 ++---- .../post-language-switch.tsx | 2 +- .../post-mainnavigation.scss | 24 +----- packages/components/src/index.html | 22 +++++- .../header/components/header.markup.ts | 22 +++++- packages/styles/src/components/_index.scss | 1 + .../styles/src/components/header/_mixins.scss | 34 ++++++++ .../styles/src/components/header/index.scss | 60 +++++++++++++++ 10 files changed, 213 insertions(+), 85 deletions(-) create mode 100644 packages/styles/src/components/header/_mixins.scss create mode 100644 packages/styles/src/components/header/index.scss diff --git a/packages/components/src/components/post-header/post-header.scss b/packages/components/src/components/post-header/post-header.scss index 8bea0377cf..933157d216 100644 --- a/packages/components/src/components/post-header/post-header.scss +++ b/packages/components/src/components/post-header/post-header.scss @@ -9,11 +9,21 @@ :host { --global-header-height: 72px; + --global-header-minimal-height: 24px; --main-header-height: 56px; --header-height: calc(var(--global-header-height) + var(--main-header-height)); + @include media.min(lg) { + display: block; + position: sticky; + inset-inline: 0; + inset-block-start: calc(-1 * (var(--global-header-height) + var(--main-header-height) - var(--global-header-minimal-height))); + box-shadow: var(--post-core-elevation-3); + } + @include media.max(lg) { --global-header-height: 64px; + --main-header-height: 48px; } } @@ -31,18 +41,17 @@ justify-content: space-between; align-items: center; position: sticky; - z-index: 2; - padding-inline-start: 4px; - padding-inline-end: 12px; - + padding-inline: var(--post-core-dimension-4); height: var(--global-header-height); + z-index: 1; @include media.max(lg) { - top: 0; + inset-block-start: 0; } @include media.min(lg) { - top: calc((var(--global-header-height) - 24px) * -1); + padding-inline-end: var(--post-core-dimension-12); + top: calc((var(--global-header-height) - var(--global-header-minimal-height)) * -1); } } @@ -53,7 +62,7 @@ slot[name='post-logo'] { .global-sub { display: flex; align-items: center; - gap: 2rem; + gap: var(--post-core-dimension-24); height: var(--global-header-height); } @@ -65,7 +74,7 @@ slot[name='post-logo'] { flex: 1 0 auto; height: var(--global-header-height); width: var(--global-header-height); - min-height: 24px; + min-height: var(--global-header-minimal-height); align-self: flex-end; @include media.min(lg) { @@ -82,25 +91,38 @@ slot[name='post-logo'] { } .title-header { - position: relative; - z-index: 1; display: flex; align-items: center; - padding-inline: 12px; - background: white; + gap: var(--post-core-dimension-4); height: var(--main-header-height); + background: var(--post-core-color-brand-white); + + @include media.min(lg) { + padding: var(--post-core-dimension-18) var(--post-core-dimension-16) var(--post-core-dimension-4) var(--post-core-dimension-12); + } @include media.max(lg) { - border-bottom: 1px solid black; + position: sticky; + z-index: 1; + inset-block-start: var(--global-header-height); + padding-inline: var(--post-core-dimension-8) var(--post-core-dimension-16); } } + :host(:not(:has([slot='title']))) .title-header { display: none; } ::slotted(h1) { margin: 0 !important; - font-size: 28px !important; + + @include media.min(lg) { + font-size: var(--post-core-font-size-28) !important; + } + + @include media.max(lg) { + font-size: var(--post-core-font-size-20) !important; + } } .mobile-toggle { @@ -111,36 +133,35 @@ slot[name='post-logo'] { .navigation { background: var(--post-core-color-brand-white); - box-shadow: var(--post-core-elevation-3); + + @include media.min(lg) { + position: sticky; + z-index: 1; + inset-block-start: var(--global-header-minimal-height); + } } // only for tablet and mobile @include media.max(lg) { .navigation { - position: absolute; + position: fixed; inset-inline: 0; inset-block-end: calc(100vh - var(--header-height)); - transition: transform animation.$transition-time-area-medium animation.$transition-easing-accelerate; - - &.extended { - transform: translateY(100%); - transition-timing-function: animation.$transition-easing-decelerate; - } + box-shadow: var(--post-core-elevation-3); + min-height: var(--post-core-dimension-10); // needed for the box-shadow to always show + max-height: calc(100vh - var(--header-height)); + overflow: auto; } ::slotted(post-mainnavigation), .navigation-footer { - display: flex; + display: none; flex-direction: column; padding-block: var(--post-core-dimension-16) var(--post-core-dimension-24); padding-inline: var(--post-core-dimension-32); - opacity: 0; - transition-property: opacity; - transition-delay: animation.$transition-time-area-medium; .navigation.extended & { - opacity: 1; - transition-delay: 0s; + display: flex; } } diff --git a/packages/components/src/components/post-header/post-header.tsx b/packages/components/src/components/post-header/post-header.tsx index ac74a52518..01fa94263b 100644 --- a/packages/components/src/components/post-header/post-header.tsx +++ b/packages/components/src/components/post-header/post-header.tsx @@ -1,7 +1,8 @@ -import { Component, h, Host, State, Element, Method } from '@stencil/core'; +import { Component, h, Host, State, Element, Method, Watch } from '@stencil/core'; import { throttle } from 'throttle-debounce'; import { version } from '@root/package.json'; import { SwitchVariant } from '@/components'; +import { slideDown, slideUp } from '@/animations/slide'; type DEVICE_SIZE = 'mobile' | 'tablet' | 'desktop' | null; @@ -11,11 +12,8 @@ type DEVICE_SIZE = 'mobile' | 'tablet' | 'desktop' | null; styleUrl: './post-header.scss', }) export class PostHeader { - @Element() host: HTMLPostHeaderElement; - @State() device: DEVICE_SIZE = null; - @State() mobileMenuExtended: boolean = false; - private scrollParent = null; + private mobileMenu: HTMLElement; private throttledScroll = () => this.handleScrollEvent(); private throttledResize = throttle(50, () => this.handleResize()); @@ -27,14 +25,30 @@ export class PostHeader { this.handleScrollEvent(); } + @Element() host: HTMLPostHeaderElement; + + @State() device: DEVICE_SIZE = null; + @State() mobileMenuExtended: boolean = false; + + @Watch('mobileMenuExtended') + frozeBody(isMobileMenuExtended: boolean) { + document.body.style.overflow = isMobileMenuExtended ? 'hidden' : ''; + } + /** * Toggles the mobile navigation. */ @Method() async toggleMobileMenu() { - if (this.device !== 'desktop') { - this.mobileMenuExtended = !this.mobileMenuExtended; + if (this.device === 'desktop') return; + + if (this.mobileMenuExtended) { + await slideUp(this.mobileMenu).finished; + } else { + slideDown(this.mobileMenu); } + + this.mobileMenuExtended = !this.mobileMenuExtended; } private handleScrollEvent() { @@ -125,7 +139,6 @@ export class PostHeader { -
@@ -133,8 +146,7 @@ export class PostHeader {
- -
+
(this.mobileMenu = el)} class={navigationClasses.join(' ')}> {(this.device === 'mobile' || this.device === 'tablet') && ( diff --git a/packages/components/src/components/post-language-switch/post-language-switch.scss b/packages/components/src/components/post-language-switch/post-language-switch.scss index d4c9266c8a..f0cc236912 100644 --- a/packages/components/src/components/post-language-switch/post-language-switch.scss +++ b/packages/components/src/components/post-language-switch/post-language-switch.scss @@ -2,6 +2,7 @@ @use '@swisspost/design-system-styles/functions/tokens'; @use '@swisspost/design-system-styles/mixins/button' as button-mx; @use '@swisspost/design-system-styles/mixins/utilities' as utilities-mx; +@use '@swisspost/design-system-styles/components/header/mixins' as header-mx; tokens.$default-map: components.$post-button; @@ -16,25 +17,16 @@ tokens.$default-map: components.$post-button; .post-language-switch-trigger { cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; - border-width: tokens.get('button-border-width'); - border-radius: tokens.get('button-border-radius-round'); - background-color: transparent; - font-family: inherit; - font-weight: tokens.get('button-label-font-weight'); - @include button-mx.button-size(sm); - - &:disabled { - border-style: tokens.get('button-border-style-disabled'); - @include button-mx.button-variant-def('disabled', 'tertiary'); - } + @include button-mx.reset-button; + @include header-mx.subsidiary-header-interactive; @include utilities-mx.focus-style; - @include button-mx.button-variant-def('enabled', 'tertiary'); - @include utilities-mx.not-disabled-hover() { @include button-mx.button-variant-def('hover', 'tertiary'); } + + post-icon { + height: var(--post-core-dimension-16); + width: var(--post-core-dimension-16); + } } diff --git a/packages/components/src/components/post-language-switch/post-language-switch.tsx b/packages/components/src/components/post-language-switch/post-language-switch.tsx index a34fe37267..c26cf8b7f8 100644 --- a/packages/components/src/components/post-language-switch/post-language-switch.tsx +++ b/packages/components/src/components/post-language-switch/post-language-switch.tsx @@ -130,7 +130,7 @@ export class PostLanguageSwitch { aria-label={`${this.caption}, ${this.description}`} > {this.activeLang.toUpperCase()} - + diff --git a/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss b/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss index 7ac5a14022..a29c54d64d 100644 --- a/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss +++ b/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss @@ -1,11 +1,10 @@ @use '@swisspost/design-system-styles/mixins/button'; @use '@swisspost/design-system-styles/mixins/icons'; @use '@swisspost/design-system-styles/mixins/media'; -@use '@swisspost/design-system-styles/mixins/utilities'; -@use '@swisspost/design-system-styles/functions/icons' as icon-fn; @use '@swisspost/design-system-styles/functions/tokens'; @use '@swisspost/design-system-styles/tokens/elements'; @use '@swisspost/design-system-styles/variables/animation'; +@use '@swisspost/design-system-styles/components/header/mixins' as header-mx; $nav-height: var(--post-core-dimension-56); @@ -111,26 +110,7 @@ post-mainnavigation { post-list-item { a, button { - width: 100%; - height: var(--post-core-dimension-48); - padding-inline-end: var(--post-core-dimension-6); - gap: var(--post-core-dimension-16); - border-block: var(--post-core-dimension-1) solid transparent; - border-block-end-color: currentColor; - font-weight: var(--post-core-font-weight-700); - - &:hover, - &.selected { - border-block-width: var(--post-core-dimension-3); - } - - &:hover::after { - content: ''; - display: block; - @include icons.icon(3020); - width: var(--post-core-dimension-24); - height: var(--post-core-dimension-24); - } + @include header-mx.mobile-header-interactive; } } } diff --git a/packages/components/src/index.html b/packages/components/src/index.html index dfa6592340..112991f4e6 100644 --- a/packages/components/src/index.html +++ b/packages/components/src/index.html @@ -22,12 +22,16 @@ - = Menu + + Menu + + + Application title diff --git a/packages/documentation/src/stories/components/header/components/header.markup.ts b/packages/documentation/src/stories/components/header/components/header.markup.ts index 5c890e2916..37813dd19d 100644 --- a/packages/documentation/src/stories/components/header/components/header.markup.ts +++ b/packages/documentation/src/stories/components/header/components/header.markup.ts @@ -6,12 +6,16 @@ export default html` - = Menu + + Menu + + + diff --git a/packages/styles/src/components/_index.scss b/packages/styles/src/components/_index.scss index 2f9f104eca..54a7d43734 100644 --- a/packages/styles/src/components/_index.scss +++ b/packages/styles/src/components/_index.scss @@ -27,6 +27,7 @@ @use 'switch'; @use 'form-hint'; @use 'form-input'; +@use 'header'; @use 'icon-button'; @use 'icon-close-button'; @use 'lead'; diff --git a/packages/styles/src/components/header/_mixins.scss b/packages/styles/src/components/header/_mixins.scss new file mode 100644 index 0000000000..c033808a6c --- /dev/null +++ b/packages/styles/src/components/header/_mixins.scss @@ -0,0 +1,34 @@ +@use '../../mixins/icons'; + +@mixin subsidiary-header-interactive() { + text-decoration: none; + display: inline-flex; + align-items: center; + gap: var(--post-core-dimension-6); + border-radius: var(--post-core-dimension-24); + font-size: var(--post-core-font-size-16); + padding: var(--post-core-dimension-3) var(--post-core-dimension-10); +} + +@mixin mobile-header-interactive() { + width: 100%; + height: var(--post-core-dimension-48); + padding-inline-end: var(--post-core-dimension-6); + gap: var(--post-core-dimension-16); + border-block: var(--post-core-dimension-1) solid transparent; + border-block-end-color: currentColor; + font-weight: var(--post-core-font-weight-700); + + &:hover, + &.selected { + border-block-width: var(--post-core-dimension-3); + } + + &:hover::after { + content: ''; + display: block; + @include icons.icon(3020); + width: var(--post-core-dimension-24); + height: var(--post-core-dimension-24); + } +} diff --git a/packages/styles/src/components/header/index.scss b/packages/styles/src/components/header/index.scss new file mode 100644 index 0000000000..674e266c8e --- /dev/null +++ b/packages/styles/src/components/header/index.scss @@ -0,0 +1,60 @@ +@use '../../mixins/media'; +@use '../../mixins/utilities'; + +@use 'mixins' as *; + +post-header { + ul[slot="meta-navigation"] { + gap: var(--post-core-dimension-4); + + @include media.max(lg) { + flex-direction: column; + + a, + button { + justify-content: space-between; + border-radius: 0; + @include mobile-header-interactive; + + @include utilities.focus-style-custom { + border-radius: var(--post-core-dimension-4); + } + } + } + } + + a, + post-togglebutton { + &:not(post-mainnavigation *) { + @include subsidiary-header-interactive; + + @include media.min(sm) { + post-icon { + height: var(--post-core-dimension-22); + width: var(--post-core-dimension-22); + } + } + + @include media.max(sm) { + padding: var(--post-core-dimension-8); + + post-icon { + height: var(--post-core-dimension-24); + width: var(--post-core-dimension-24); + } + + .visually-hidden-sm { + @include utilities.visuallyhidden; + } + } + } + } + + a.selected, + post-togglebutton[aria-pressed='true'] { + &:not(post-mainnavigation *) { + color: var(--post-core-color-brand-white); + background: var(--post-core-color-sandgrey-100); + } + } +} From a9983eb48ba4c43610e6ad3e0ba9bfb285c6e45b Mon Sep 17 00:00:00 2001 From: Alona Zherdetska <138328641+alionazherdetska@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:15:07 +0100 Subject: [PATCH 12/13] chore(styles): remove deprecated portal styles (#4186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …erences --- .changeset/nervous-lizards-laugh.md | 5 +++++ packages/styles/src/index.scss | 3 --- packages/styles/src/layouts/portal/_index.scss | 1 - packages/styles/src/layouts/portal/_subnavigation.scss | 5 ----- packages/styles/tests/layouts/portal/index.test.scss | 1 - 5 files changed, 5 insertions(+), 10 deletions(-) create mode 100644 .changeset/nervous-lizards-laugh.md delete mode 100644 packages/styles/src/layouts/portal/_index.scss delete mode 100644 packages/styles/src/layouts/portal/_subnavigation.scss delete mode 100644 packages/styles/tests/layouts/portal/index.test.scss diff --git a/.changeset/nervous-lizards-laugh.md b/.changeset/nervous-lizards-laugh.md new file mode 100644 index 0000000000..944f941bc9 --- /dev/null +++ b/.changeset/nervous-lizards-laugh.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-styles': minor +--- + +Removed outdated portal-specific styles, including subnavigation-related rules. diff --git a/packages/styles/src/index.scss b/packages/styles/src/index.scss index 97fa256ba6..2f92769c20 100644 --- a/packages/styles/src/index.scss +++ b/packages/styles/src/index.scss @@ -4,6 +4,3 @@ @use './utilities'; @use './elements'; @use './components'; - -// Portal specific styles -@use './layouts/portal'; diff --git a/packages/styles/src/layouts/portal/_index.scss b/packages/styles/src/layouts/portal/_index.scss deleted file mode 100644 index c067e7178e..0000000000 --- a/packages/styles/src/layouts/portal/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@use './subnavigation'; diff --git a/packages/styles/src/layouts/portal/_subnavigation.scss b/packages/styles/src/layouts/portal/_subnavigation.scss deleted file mode 100644 index 538ea92584..0000000000 --- a/packages/styles/src/layouts/portal/_subnavigation.scss +++ /dev/null @@ -1,5 +0,0 @@ -.subnavigation-list { - width: 100%; - margin: 0; - padding: 0; -} diff --git a/packages/styles/tests/layouts/portal/index.test.scss b/packages/styles/tests/layouts/portal/index.test.scss deleted file mode 100644 index 477d1c3bf2..0000000000 --- a/packages/styles/tests/layouts/portal/index.test.scss +++ /dev/null @@ -1 +0,0 @@ -@use 'src/layouts/portal'; From 4887a7852277f4a0bfcb921348c06331039e9261 Mon Sep 17 00:00:00 2001 From: Alona Zherdetska <138328641+alionazherdetska@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:15:25 +0100 Subject: [PATCH 13/13] chore(component): commit missing auto-generated files for `post-language-switch` and `post-language-option` (#4238) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …age-switch` --- packages/components/src/components.d.ts | 16 ---------------- .../components/post-language-option/readme.md | 15 +++++++-------- .../components/post-language-switch/readme.md | 1 - 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/packages/components/src/components.d.ts b/packages/components/src/components.d.ts index fc3cb9961f..ba620094f3 100644 --- a/packages/components/src/components.d.ts +++ b/packages/components/src/components.d.ts @@ -222,10 +222,6 @@ export namespace Components { * The ISO 639 language code, formatted according to [RFC 5646 (also known as BCP 47)](https://datatracker.ietf.org/doc/html/rfc5646). For example, "de". */ "code": string; - /** - * Used on parent component (post-language-switch) to detect elements that are manually added - */ - "generated": boolean; /** * The full name of the language. For example, "Deutsch". */ @@ -252,10 +248,6 @@ export namespace Components { * A descriptive text for the list of language options */ "description": string; - /** - * The name of the language switch, which will be used on the dropdown as an ID - */ - "name": string; /** * Variant that determines the rendering of the language switch either as a list (used on mobile in the header) or a dropdown (used on desktop in the header) */ @@ -1050,10 +1042,6 @@ declare namespace LocalJSX { * The ISO 639 language code, formatted according to [RFC 5646 (also known as BCP 47)](https://datatracker.ietf.org/doc/html/rfc5646). For example, "de". */ "code": string; - /** - * Used on parent component (post-language-switch) to detect elements that are manually added - */ - "generated"?: boolean; /** * The full name of the language. For example, "Deutsch". */ @@ -1080,10 +1068,6 @@ declare namespace LocalJSX { * A descriptive text for the list of language options */ "description"?: string; - /** - * The name of the language switch, which will be used on the dropdown as an ID - */ - "name"?: string; /** * Variant that determines the rendering of the language switch either as a list (used on mobile in the header) or a dropdown (used on desktop in the header) */ diff --git a/packages/components/src/components/post-language-option/readme.md b/packages/components/src/components/post-language-option/readme.md index 1b9514058f..8aff93a8a1 100644 --- a/packages/components/src/components/post-language-option/readme.md +++ b/packages/components/src/components/post-language-option/readme.md @@ -7,14 +7,13 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ----------- | -| `active` | `active` | If set to `true`, the language option is considered the current language for the page. | `boolean` | `undefined` | -| `code` _(required)_ | `code` | The ISO 639 language code, formatted according to [RFC 5646 (also known as BCP 47)](https://datatracker.ietf.org/doc/html/rfc5646). For example, "de". | `string` | `undefined` | -| `generated` | `generated` | Used on parent component (post-language-switch) to detect elements that are manually added | `boolean` | `undefined` | -| `name` | `name` | The full name of the language. For example, "Deutsch". | `string` | `undefined` | -| `url` | `url` | The URL used for the href attribute of the internal anchor. This field is optional; if not provided, a button will be used internally instead of an anchor. | `string` | `undefined` | -| `variant` | `variant` | The variant of the post-language-switch parent (dynamically set by the parent) | `"dropdown" \| "list"` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ----------- | +| `active` | `active` | If set to `true`, the language option is considered the current language for the page. | `boolean` | `undefined` | +| `code` _(required)_ | `code` | The ISO 639 language code, formatted according to [RFC 5646 (also known as BCP 47)](https://datatracker.ietf.org/doc/html/rfc5646). For example, "de". | `string` | `undefined` | +| `name` | `name` | The full name of the language. For example, "Deutsch". | `string` | `undefined` | +| `url` | `url` | The URL used for the href attribute of the internal anchor. This field is optional; if not provided, a button will be used internally instead of an anchor. | `string` | `undefined` | +| `variant` | `variant` | The variant of the post-language-switch parent (dynamically set by the parent) | `"dropdown" \| "list"` | `undefined` | ## Events diff --git a/packages/components/src/components/post-language-switch/readme.md b/packages/components/src/components/post-language-switch/readme.md index c2079be7d3..8beb610dfa 100644 --- a/packages/components/src/components/post-language-switch/readme.md +++ b/packages/components/src/components/post-language-switch/readme.md @@ -9,7 +9,6 @@ | ------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ----------- | | `caption` | `caption` | A title for the list of language options | `string` | `undefined` | | `description` | `description` | A descriptive text for the list of language options | `string` | `undefined` | -| `name` | `name` | The name of the language switch, which will be used on the dropdown as an ID | `string` | `undefined` | | `variant` | `variant` | Variant that determines the rendering of the language switch either as a list (used on mobile in the header) or a dropdown (used on desktop in the header) | `"dropdown" \| "list"` | `'list'` |