From 817459353f206f24ac12fa7c51db9406adafb9b7 Mon Sep 17 00:00:00 2001 From: Lea Date: Wed, 30 Oct 2024 07:51:17 +0100 Subject: [PATCH] feat(styles): component form footer (#3616) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alizé Debray <33580481+alizedebray@users.noreply.github.com> --- .changeset/gold-chairs-grin.md | 6 + .../components/form-footer.snapshot.ts | 7 + .../forms/form-footer/form-footer.docs.mdx | 22 +++ .../form-footer.snapshot.stories.ts | 70 +++++++++ .../forms/form-footer/form-footer.stories.ts | 146 ++++++++++++++++++ .../src/stories/patterns/forms/forms.docs.mdx | 6 + .../stories/patterns/forms/forms.stories.ts | 6 + packages/styles/src/components/_index.scss | 1 + .../styles/src/components/form-footer.scss | 18 +++ packages/styles/src/mixins/_utilities.scss | 26 ++++ 10 files changed, 308 insertions(+) create mode 100644 .changeset/gold-chairs-grin.md create mode 100644 packages/documentation/cypress/snapshots/components/form-footer.snapshot.ts create mode 100644 packages/documentation/src/stories/components/forms/form-footer/form-footer.docs.mdx create mode 100644 packages/documentation/src/stories/components/forms/form-footer/form-footer.snapshot.stories.ts create mode 100644 packages/documentation/src/stories/components/forms/form-footer/form-footer.stories.ts create mode 100644 packages/styles/src/components/form-footer.scss diff --git a/.changeset/gold-chairs-grin.md b/.changeset/gold-chairs-grin.md new file mode 100644 index 0000000000..6ed049af75 --- /dev/null +++ b/.changeset/gold-chairs-grin.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-documentation': minor +'@swisspost/design-system-styles': minor +--- + +Added Form Footer component. diff --git a/packages/documentation/cypress/snapshots/components/form-footer.snapshot.ts b/packages/documentation/cypress/snapshots/components/form-footer.snapshot.ts new file mode 100644 index 0000000000..31594064b4 --- /dev/null +++ b/packages/documentation/cypress/snapshots/components/form-footer.snapshot.ts @@ -0,0 +1,7 @@ +describe('Form footer', () => { + it('default', () => { + cy.visit('/iframe.html?id=snapshots--form-footer'); + cy.get('.form-footer post-icon', { timeout: 30000 }).should('be.visible'); + cy.percySnapshot('Form footer', { widths: [320, 1440] }); + }); +}); diff --git a/packages/documentation/src/stories/components/forms/form-footer/form-footer.docs.mdx b/packages/documentation/src/stories/components/forms/form-footer/form-footer.docs.mdx new file mode 100644 index 0000000000..c76cbddae2 --- /dev/null +++ b/packages/documentation/src/stories/components/forms/form-footer/form-footer.docs.mdx @@ -0,0 +1,22 @@ +import { Canvas, Controls, Meta } from '@storybook/blocks'; +import * as FormFooterStories from './form-footer.stories'; +import StylesPackageImport from '@/shared/styles-package-import.mdx'; + + + +
+ # Footer + + +
+ +
+ The form footer is placed at the end/bottom of a form or a form step, in case the form is splitted with a stepper. It provides workflow-related actions to the form or form step. +
+ + +
+ +
diff --git a/packages/documentation/src/stories/components/forms/form-footer/form-footer.snapshot.stories.ts b/packages/documentation/src/stories/components/forms/form-footer/form-footer.snapshot.stories.ts new file mode 100644 index 0000000000..da48af311d --- /dev/null +++ b/packages/documentation/src/stories/components/forms/form-footer/form-footer.snapshot.stories.ts @@ -0,0 +1,70 @@ +import type { StoryObj } from '@storybook/web-components'; +import meta, { FooterArgs } from './form-footer.stories'; +import { html } from 'lit'; +import { bombArgs } from '@/utils'; + +const { id, ...metaWithoutId } = meta; + +export default { + ...metaWithoutId, + title: 'Snapshots', +}; + +type Story = StoryObj; + +export const FormFooter: Story = { + render: () => { + return html` +
+ ${['bg-white', 'bg-dark'].map( + bg => html` +
+ ${bombArgs({ + showPrimaryButton: [true, false], + showSecondaryButton: [true, false], + showTertiaryButton: [true, false], + }).map(args => { + const primaryButton = args.showPrimaryButton + ? html`` + : null; + const secondaryButton = args.showSecondaryButton + ? html`` + : null; + + return html` + + `; + })} +
+ `, + )} +
+ `; + }, +}; diff --git a/packages/documentation/src/stories/components/forms/form-footer/form-footer.stories.ts b/packages/documentation/src/stories/components/forms/form-footer/form-footer.stories.ts new file mode 100644 index 0000000000..a2ce414be9 --- /dev/null +++ b/packages/documentation/src/stories/components/forms/form-footer/form-footer.stories.ts @@ -0,0 +1,146 @@ +import { Args, StoryObj } from '@storybook/web-components'; +import { html } from 'lit'; +import { MetaComponent } from '@root/types'; + +export const FooterArgs = { + showPrimaryButton: true, + primaryButtonText: 'Send', + primaryButtonIcon: '3020', + showSecondaryButton: true, + secondaryButtonText: 'Cancel', + showTertiaryButton: true, + tertiaryButtonText: 'Back', + tertiaryButtonIcon: '3024', +}; + +const meta: MetaComponent = { + id: 'f2eddf67-2c3c-40c4-bfec-df49bd028001', + title: 'Components/Forms/Form Footer', + tags: ['package:HTML'], + render: render, + parameters: { + badges: [], + design: { + type: 'figma', + url: 'https://www.figma.com/design/JIT5AdGYqv6bDRpfBPV8XR/Foundations-%26-Components-Next-Level?node-id=1498-28215', + }, + }, + args: { + ...FooterArgs, + }, + argTypes: { + showPrimaryButton: { + name: 'Show primary button', + description: 'Show or hide the primary button (last one on the right)', + control: { type: 'boolean' }, + table: { + category: 'Primary button', + }, + }, + primaryButtonText: { + name: 'Primary button text', + description: 'Text to display on the primary button', + control: { type: 'text' }, + table: { + category: 'Primary button', + }, + if: { + arg: 'showPrimaryButton', + }, + }, + primaryButtonIcon: { + name: 'Primary button icon', + description: 'Icon to display on the primary button', + control: { type: 'text' }, + table: { + category: 'Primary button', + }, + if: { + arg: 'showPrimaryButton', + }, + }, + showSecondaryButton: { + name: 'Show secondary button', + description: 'Show or hide the secondary button (first one on the right)', + control: { type: 'boolean' }, + table: { + category: 'Secondary button', + }, + }, + secondaryButtonText: { + name: 'Secondary button text', + description: 'Text to display on the secondary button', + control: { type: 'text' }, + table: { + category: 'Secondary button', + }, + if: { + arg: 'showSecondaryButton', + }, + }, + showTertiaryButton: { + name: 'Show tertiary button', + description: 'Show or hide the tertiary button (button on the left)', + control: { type: 'boolean' }, + table: { + category: 'Tertiary button', + }, + }, + tertiaryButtonText: { + name: 'Tertiary button text', + description: 'Text to display on the tertiary button', + control: { type: 'text' }, + table: { + category: 'Tertiary button', + }, + if: { + arg: 'showTertiaryButton', + }, + }, + tertiaryButtonIcon: { + name: 'Tertiary button icon', + description: 'Icon to display on the tertiary button', + control: { type: 'text' }, + table: { + category: 'Tertiary button', + }, + if: { + arg: 'showTertiaryButton', + }, + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export function render(args: Args) { + const primaryButton = args.showPrimaryButton + ? html`` + : null; + const secondaryButton = args.showSecondaryButton + ? html`` + : null; + + return html` + + `; +} + +export const Default: Story = {}; diff --git a/packages/documentation/src/stories/patterns/forms/forms.docs.mdx b/packages/documentation/src/stories/patterns/forms/forms.docs.mdx index 132302c9ea..422328226b 100644 --- a/packages/documentation/src/stories/patterns/forms/forms.docs.mdx +++ b/packages/documentation/src/stories/patterns/forms/forms.docs.mdx @@ -96,3 +96,9 @@ To enable screen-readers to detect and read your hints, link the `` with the aria-describedby attribute to the hint via id. + +## Form footer + +The form footer is placed at the end/bottom of a form or a form step, in case the form is splitted with a stepper. It provides workflow-related actions to the form or form step. + + diff --git a/packages/documentation/src/stories/patterns/forms/forms.stories.ts b/packages/documentation/src/stories/patterns/forms/forms.stories.ts index c5f6c06d6f..46cb6db6e1 100644 --- a/packages/documentation/src/stories/patterns/forms/forms.stories.ts +++ b/packages/documentation/src/stories/patterns/forms/forms.stories.ts @@ -1,6 +1,7 @@ import { StoryObj } from '@storybook/web-components'; import { html } from 'lit'; import { MetaExtended } from '@root/types'; +import * as FormFooterMeta from '../../components/forms/form-footer/form-footer.stories'; const meta: MetaExtended = { id: 'd83829b2-7de2-48d2-be64-07a80c9caef3', @@ -352,3 +353,8 @@ export const Hints: Story = { `, }; + +export const Footer: Story = { + render: FormFooterMeta.render, + args: FormFooterMeta.FooterArgs, +}; diff --git a/packages/styles/src/components/_index.scss b/packages/styles/src/components/_index.scss index d7a6057fd8..3640c67905 100644 --- a/packages/styles/src/components/_index.scss +++ b/packages/styles/src/components/_index.scss @@ -15,6 +15,7 @@ @use 'close'; @use 'elevation'; @use 'error-container'; +@use 'form-footer'; @use 'form-range'; @use 'form-select'; @use 'form-textarea'; diff --git a/packages/styles/src/components/form-footer.scss b/packages/styles/src/components/form-footer.scss new file mode 100644 index 0000000000..42ae499e69 --- /dev/null +++ b/packages/styles/src/components/form-footer.scss @@ -0,0 +1,18 @@ +@use '../mixins/utilities' as utility-mx; +@use '../tokens/components'; +@use '../functions/tokens' as tokens; + +tokens.$default-map: components.$post-form-footer; + +.form-footer { + @include utility-mx.responsive-actions(); + border-block-start-width: tokens.get('form-footer-border-block-start-width'); + border-block-start-color: tokens.get('form-footer-border-start-color'); + border-block-start-style: tokens.get('form-footer-border-block-start-style'); + padding-block-start: tokens.get('form-footer-padding-block-start'); + gap: tokens.get('form-footer-gap'); + + &-primary-actions { + gap: tokens.get('form-footer-gap'); + } +} diff --git a/packages/styles/src/mixins/_utilities.scss b/packages/styles/src/mixins/_utilities.scss index c53dd8740f..003aff176c 100644 --- a/packages/styles/src/mixins/_utilities.scss +++ b/packages/styles/src/mixins/_utilities.scss @@ -1,5 +1,7 @@ +@use './../themes/bootstrap/core' as *; @use '../variables/spacing'; @use '../variables/commons'; +@use '../variables/breakpoints'; @mixin reset-list() { margin: 0; @@ -174,3 +176,27 @@ } } } + +@mixin responsive-actions { + display: flex; + flex-direction: column; + justify-content: space-between; + + @include media-breakpoint-up(md) { + flex-direction: row-reverse; + + > .btn { + margin-right: auto; + } + } + + &-primary-actions { + display: flex; + flex-direction: column; + + @include media-breakpoint-up(md) { + flex-direction: row-reverse; + margin-left: auto; + } + } +}