From 91662360bbcab6e834e364dd8556f63ad12e81a6 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Thu, 8 Aug 2024 15:29:47 +0200 Subject: [PATCH 1/4] feat(sbb-form-field): introduce size s --- .../form-field/form-field/form-field.scss | 67 +++++++++++---- .../form-field/form-field.stories.ts | 84 ++++++++++++++++++- .../form-field/form-field/form-field.ts | 2 +- .../form-field/form-field.visual.spec.ts | 2 +- src/elements/form-field/form-field/readme.md | 15 ++++ .../fullscreen-diff/fullscreen-diff.scss | 4 +- 6 files changed, 153 insertions(+), 21 deletions(-) diff --git a/src/elements/form-field/form-field/form-field.scss b/src/elements/form-field/form-field/form-field.scss index fad0d2f1c7..9ae795d5ef 100644 --- a/src/elements/form-field/form-field/form-field.scss +++ b/src/elements/form-field/form-field/form-field.scss @@ -25,19 +25,25 @@ var(--sbb-icon-svg-width) + var(--sbb-form-field-gap) ); --sbb-form-field-overflow: hidden; + --sbb-form-field-input-text-size: var(--sbb-font-size-text-m); + --sbb-form-field-label-text-size: var(--sbb-font-size-text-xs); --sbb-form-field-label-size: calc( - var(--sbb-font-size-text-xs) * var(--sbb-typo-line-height-body-text) + var(--sbb-form-field-label-text-size) * var(--sbb-typo-line-height-body-text) ); - --sbb-form-field-input-size: calc( - var(--sbb-font-size-text-m) * var(--sbb-typo-line-height-body-text) + --sbb-form-field-text-line-height: calc( + var(--sbb-form-field-input-text-size) * var(--sbb-typo-line-height-body-text) ); --sbb-form-field-margin-block-start: calc( ( var(--sbb-form-field-min-height) - var(--sbb-form-field-label-size) - var( - --sbb-form-field-input-size + --sbb-form-field-text-line-height ) + var(--sbb-form-field-label-to-input-overlapping) ) / 2 ); + --sbb-form-field-spacer-margin-block-end: calc( + -1 * var(--sbb-form-field-label-to-input-overlapping) + ); + --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(8.5)}; // Lock sbb-icon size --sbb-icon-svg-width: var(--sbb-size-icon-ui-small); @@ -47,6 +53,10 @@ // to default color for cases where the form field is used in a negative context. --sbb-focus-outline-color: var(--sbb-focus-outline-color-default); + @include sbb.mq($from: medium) { + --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(10.5)}; + } + @include sbb.if-forced-colors { --sbb-form-field-border-color: ButtonBorder; } @@ -71,6 +81,25 @@ } } +:host([size='s']) { + --sbb-form-field-min-height: var(--sbb-size-element-xs); + --sbb-form-field-padding-inline: var(--sbb-spacing-fixed-2x); + --sbb-form-field-input-text-size: var(--sbb-font-size-text-s); + --sbb-form-field-label-text-size: var(--sbb-font-size-text-xxs); + + // Values found by try and error + --sbb-form-field-label-to-input-overlapping: #{sbb.px-to-rem-build(10)}; + --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(5.125)}; + --sbb-form-field-spacer-margin-block-end: #{sbb.px-to-rem-build(-11)}; + + @include sbb.mq($from: medium) { + // Values found by try and error + --sbb-form-field-label-to-input-overlapping: #{sbb.px-to-rem-build(11)}; + --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(6.5)}; + --sbb-form-field-spacer-margin-block-end: #{sbb.px-to-rem-build(-8)}; + } +} + :host([size='l']) { --sbb-form-field-min-height: var(--sbb-size-element-l); --sbb-form-field-padding-inline: var(--sbb-spacing-responsive-xxxs); @@ -284,10 +313,10 @@ .sbb-form-field__label-spacer { display: flex; - height: calc(var(--sbb-font-size-text-xs) * var(--sbb-typo-line-height-body-text)); + height: calc(var(--sbb-form-field-label-text-size) * var(--sbb-typo-line-height-body-text)); // Moves label down and input up to meet positioning requirements - margin-block-end: calc(-1 * var(--sbb-form-field-label-to-input-overlapping)); + margin-block-end: var(--sbb-form-field-spacer-margin-block-end); } // To avoid doubled payload, we group the rules. @@ -302,8 +331,6 @@ } .sbb-form-field__label { - @include sbb.text-xs--regular; - display: flex; max-width: 100%; cursor: default; @@ -311,6 +338,12 @@ inset-block-start: 0; color: var(--sbb-form-field-label-color); + @include sbb.text-xs--regular; + + :host([size='s']) & { + @include sbb.text-xxs--regular; + } + :host([data-input-type='select']) &, :host([data-input-type='sbb-select']) & { padding-inline-end: var(--sbb-form-field-select-inline-padding-end); @@ -340,12 +373,8 @@ )[data-input-empty] ) & { - font-size: var(--sbb-font-size-text-m); - transform: translateY(#{sbb.px-to-rem-build(8.5)}); - - @include sbb.mq($from: medium) { - transform: translateY(#{sbb.px-to-rem-build(10.5)}); - } + font-size: var(--sbb-form-field-input-text-size); + transform: translateY(var(--sbb-form-field-floating-label-transform)); } } @@ -353,6 +382,14 @@ @include sbb.ellipsis; } +.sbb-form-field__input { + :host([size='s']:is([data-input-type='input'], [data-input-type='select'])) & { + // In size s, the natural height of the text input is too small. + // To not reserve too much space, we decrease the height. + height: var(--sbb-spacing-fixed-6x); + } +} + // Input .sbb-form-field__input ::slotted(:where(input, select, textarea, sbb-select)) { @@ -375,7 +412,7 @@ // To be more specific than the styles in normalize.scss we need to use !important // TODO: Find a better solution - font-size: var(--sbb-font-size-text-m) !important; + font-size: var(--sbb-form-field-input-text-size) !important; font-family: var(--sbb-typo-font-family) !important; line-height: var(--sbb-typo-line-height-body-text) !important; diff --git a/src/elements/form-field/form-field/form-field.stories.ts b/src/elements/form-field/form-field/form-field.stories.ts index 13c9cd57a0..c579b2f265 100644 --- a/src/elements/form-field/form-field/form-field.stories.ts +++ b/src/elements/form-field/form-field/form-field.stories.ts @@ -408,7 +408,7 @@ const size: InputType = { control: { type: 'inline-radio', }, - options: ['m', 'l'], + options: ['s', 'm', 'l'], table: { category: 'Form-field', }, @@ -458,7 +458,7 @@ const basicArgs: Args = { 'floating-label': false, optional: false, borderless: false, - size: size.options![0], + size: size.options![1], negative: false, cssClass: '', placeholder: 'Input placeholder', @@ -482,10 +482,16 @@ export const InputSizeL: StoryObj = { args: { ...basicArgs, value: 'This input value is so long that it needs ellipsis to fit.', - size: 'l', + size: size.options![2], }, }; +export const InputSizeS: StoryObj = { + render: TemplateInput, + argTypes: basicArgTypes, + args: { ...basicArgs, size: size.options![0] }, +}; + export const InputNoLabel: StoryObj = { render: TemplateInput, argTypes: basicArgTypes, @@ -528,6 +534,18 @@ export const InputOptionalAndIcons: StoryObj = { args: { ...basicArgs, optional: true }, }; +export const InputOptionalAndIconsSizeS: StoryObj = { + render: TemplateInputWithIcons, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, size: size.options![0] }, +}; + +export const InputOptionalAndIconsSizeL: StoryObj = { + render: TemplateInputWithIcons, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, size: size.options![2] }, +}; + export const InputWithMiniButton: StoryObj = { render: TemplateInputWithMiniButton, argTypes: basicArgTypes, @@ -639,6 +657,18 @@ export const SelectOptionalAndIcons: StoryObj = { args: { ...basicArgs, optional: true }, }; +export const SelectOptionalAndIconsSizeS: StoryObj = { + render: TemplateSelectWithIcons, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, size: size.options![0] }, +}; + +export const SelectOptionalAndIconsSizeL: StoryObj = { + render: TemplateSelectWithIcons, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, size: size.options![2] }, +}; + export const Textarea: StoryObj = { render: TemplateTextarea, argTypes: basicArgTypes, @@ -675,6 +705,18 @@ export const TextareaOptionalAndIcon: StoryObj = { args: { ...basicArgs, optional: true }, }; +export const TextareaOptionalAndIconSizeS: StoryObj = { + render: TemplateTextareaWithIcon, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, size: size.options![0] }, +}; + +export const TextareaOptionalAndIconSizeL: StoryObj = { + render: TemplateTextareaWithIcon, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, size: size.options![2] }, +}; + export const TextareaFloatingLabel: StoryObj = { render: TemplateTextarea, argTypes: basicArgTypes, @@ -754,6 +796,18 @@ export const InputOptionalAndIconsNegative: StoryObj = { args: { ...basicArgs, optional: true, negative: true }, }; +export const InputOptionalAndIconsNegativeSizeS: StoryObj = { + render: TemplateInputWithIcons, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, negative: true, size: size.options![0] }, +}; + +export const InputOptionalAndIconsNegativeSizeL: StoryObj = { + render: TemplateInputWithIcons, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, negative: true, size: size.options![2] }, +}; + export const InputWithMiniButtonNegative: StoryObj = { render: TemplateInputWithMiniButton, argTypes: basicArgTypes, @@ -868,6 +922,18 @@ export const SelectOptionalAndIconsNegative: StoryObj = { args: { ...basicArgs, optional: true, negative: true }, }; +export const SelectOptionalAndIconsNegativeSizeS: StoryObj = { + render: TemplateSelectWithIcons, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, negative: true, size: size.options![0] }, +}; + +export const SelectOptionalAndIconsNegativeSizeL: StoryObj = { + render: TemplateSelectWithIcons, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, negative: true, size: size.options![2] }, +}; + export const InputCollapsedWidthNegative: StoryObj = { render: TemplateInput, argTypes: basicArgTypes, @@ -916,6 +982,18 @@ export const TextareaOptionalAndIconNegative: StoryObj = { args: { ...basicArgs, optional: true, negative: true }, }; +export const TextareaOptionalAndIconNegativeSizeS: StoryObj = { + render: TemplateTextareaWithIcon, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, negative: true, size: size.options![0] }, +}; + +export const TextareaOptionalAndIconNegativeSizeL: StoryObj = { + render: TemplateTextareaWithIcon, + argTypes: basicArgTypes, + args: { ...basicArgs, optional: true, negative: true, size: size.options![2] }, +}; + export const TextareaFloatingLabelNegative: StoryObj = { render: TemplateTextarea, argTypes: basicArgTypes, diff --git a/src/elements/form-field/form-field/form-field.ts b/src/elements/form-field/form-field/form-field.ts index 7d93a9b043..a58b86d4cb 100644 --- a/src/elements/form-field/form-field/form-field.ts +++ b/src/elements/form-field/form-field/form-field.ts @@ -74,7 +74,7 @@ export class SbbFormFieldElement extends SbbNegativeMixin(SbbHydrationMixin(LitE @property({ type: Boolean }) public optional?: boolean; /** Size variant, either l or m. */ - @property({ reflect: true }) public size?: 'l' | 'm' = 'm'; + @property({ reflect: true }) public size?: 'l' | 'm' | 's' = 'm'; /** Whether to display the form field without a border. */ @property({ reflect: true, type: Boolean }) public borderless = false; diff --git a/src/elements/form-field/form-field/form-field.visual.spec.ts b/src/elements/form-field/form-field/form-field.visual.spec.ts index 825f68696b..3d79045f4f 100644 --- a/src/elements/form-field/form-field/form-field.visual.spec.ts +++ b/src/elements/form-field/form-field/form-field.visual.spec.ts @@ -143,7 +143,7 @@ describe(`sbb-form-field`, () => { }; const visualProp = { - size: ['m', 'l'], + size: ['s', 'm', 'l'], width: ['default', 'collapse'], errorText: [true, false], }; diff --git a/src/elements/form-field/form-field/readme.md b/src/elements/form-field/form-field/readme.md index b76d3d51c6..a5de8d639e 100644 --- a/src/elements/form-field/form-field/readme.md +++ b/src/elements/form-field/form-field/readme.md @@ -94,6 +94,21 @@ Please refer to their documentation for more details. ## Style +The component has a `size` property, which accepts three different values: `s`, `m` (default) and `l`. + +```html + + + + + + + + + This field is required! + +``` + By default, the component has a defined width and min-width. However, this behavior can be overridden by setting the `width` property to `collapse`: in this way the component adapts its width to the inner slotted input component. This is useful, for example, for the [sbb-time-input](/docs/elements-sbb-time-input--docs) component. diff --git a/src/visual-regression-app/src/components/test-case/image-diff/fullscreen-diff/fullscreen-diff.scss b/src/visual-regression-app/src/components/test-case/image-diff/fullscreen-diff/fullscreen-diff.scss index ded25e8815..16564d8d15 100644 --- a/src/visual-regression-app/src/components/test-case/image-diff/fullscreen-diff/fullscreen-diff.scss +++ b/src/visual-regression-app/src/components/test-case/image-diff/fullscreen-diff/fullscreen-diff.scss @@ -17,5 +17,7 @@ .app-scroll-container { @include sbb.scrollbar; - overflow: auto; + & { + overflow: auto; + } } From 36ea6487fe47475b3203f1cbdb9a9fabbf9971f8 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Thu, 8 Aug 2024 15:35:32 +0200 Subject: [PATCH 2/4] refactor: fix integrity --- src/elements/form-field/form-field/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elements/form-field/form-field/readme.md b/src/elements/form-field/form-field/readme.md index a5de8d639e..262c983270 100644 --- a/src/elements/form-field/form-field/readme.md +++ b/src/elements/form-field/form-field/readme.md @@ -157,7 +157,7 @@ technology will announce errors when they appear. | `inputElement` | - | public | `HTMLInputElement \| HTMLSelectElement \| HTMLElement \| undefined` | | Returns the input element. | | `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | | `optional` | `optional` | public | `boolean \| undefined` | | Indicates whether the input is optional. | -| `size` | `size` | public | `'l' \| 'm' \| undefined` | `'m'` | Size variant, either l or m. | +| `size` | `size` | public | `'l' \| 'm' \| 's' \| undefined` | `'m'` | Size variant, either l or m. | | `width` | `width` | public | `'default' \| 'collapse'` | `'default'` | Defines the width of the component: - `default`: the component has defined width and min-width; - `collapse`: the component adapts itself to its inner input content. | ## Methods From 6c3c02535923e8b510fbd0c8563178155295188a Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Mon, 12 Aug 2024 16:15:45 +0200 Subject: [PATCH 3/4] refactor: use new approach WIP --- src/elements/form-field/form-field/form-field.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/elements/form-field/form-field/form-field.scss b/src/elements/form-field/form-field/form-field.scss index 9ae795d5ef..e900bf3a94 100644 --- a/src/elements/form-field/form-field/form-field.scss +++ b/src/elements/form-field/form-field/form-field.scss @@ -383,11 +383,7 @@ } .sbb-form-field__input { - :host([size='s']:is([data-input-type='input'], [data-input-type='select'])) & { - // In size s, the natural height of the text input is too small. - // To not reserve too much space, we decrease the height. - height: var(--sbb-spacing-fixed-6x); - } + display: flex; } // Input @@ -458,6 +454,10 @@ min-height: calc((var(--sbb-typo-line-height-body-text) * 1em)); } + :host([size='l']) & { + padding-block-end: #{sbb.px-to-rem-build(5.5)}; + } + :host([negative]) & { @include sbb.scrollbar($negative: true); } From 2756999df32ab73ebb570ab8b886cb23ab740cb9 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Tue, 13 Aug 2024 13:37:45 +0200 Subject: [PATCH 4/4] fix: fix styles --- .../form-field/form-field/form-field.scss | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/elements/form-field/form-field/form-field.scss b/src/elements/form-field/form-field/form-field.scss index e900bf3a94..f02608a5d1 100644 --- a/src/elements/form-field/form-field/form-field.scss +++ b/src/elements/form-field/form-field/form-field.scss @@ -89,13 +89,13 @@ // Values found by try and error --sbb-form-field-label-to-input-overlapping: #{sbb.px-to-rem-build(10)}; - --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(5.125)}; - --sbb-form-field-spacer-margin-block-end: #{sbb.px-to-rem-build(-11)}; + --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(5.5)}; + --sbb-form-field-spacer-margin-block-end: #{sbb.px-to-rem-build(-8.5)}; @include sbb.mq($from: medium) { // Values found by try and error --sbb-form-field-label-to-input-overlapping: #{sbb.px-to-rem-build(11)}; - --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(6.5)}; + --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(5)}; --sbb-form-field-spacer-margin-block-end: #{sbb.px-to-rem-build(-8)}; } } @@ -338,6 +338,9 @@ inset-block-start: 0; color: var(--sbb-form-field-label-color); + // Textarea with forced colors active needs to have the label a level higher + z-index: 1; + @include sbb.text-xs--regular; :host([size='s']) & { @@ -384,6 +387,12 @@ .sbb-form-field__input { display: flex; + + :host([size='s']:is([data-input-type='input'], [data-input-type='select'])) & { + // In size s, the natural height of the text input is too small. + // To not reserve too much space, we decrease the height. + margin-block-end: #{sbb.px-to-rem-build(-2)}; + } } // Input