Skip to content

Commit

Permalink
feat: introduce button form support (#3170)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Previously the `disabledInteractive` property had to be
used along with the `disabled` property.
With this change, either `disabled` or `disabledInteractive` should be used.

---------

Co-authored-by: Davide Mininni <[email protected]>
  • Loading branch information
jeripeierSBB and DavideMininni-Fincons authored Oct 31, 2024
1 parent 3962c2c commit eeb7a0a
Show file tree
Hide file tree
Showing 113 changed files with 940 additions and 508 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ snapshots["sbb-timetable-row renders defaultTrip Shadow DOM"] =
<sbb-card-button
data-action=""
data-button=""
role="button"
slot="action"
tabindex="0"
>
Expand Down Expand Up @@ -97,7 +96,6 @@ snapshots["sbb-timetable-row renders platform Shadow DOM"] =
<sbb-card-button
data-action=""
data-button=""
role="button"
slot="action"
tabindex="0"
>
Expand Down Expand Up @@ -196,7 +194,6 @@ snapshots["sbb-timetable-row renders bus strip Shadow DOM"] =
<sbb-card-button
data-action=""
data-button=""
role="button"
slot="action"
tabindex="0"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ snapshots["sbb-accordion renders DOM"] =
data-size="l"
data-slot-names="unnamed"
id="sbb-expansion-panel-header-1"
role="button"
slot="header"
tabindex="0"
>
Expand Down Expand Up @@ -46,7 +45,6 @@ snapshots["sbb-accordion renders DOM"] =
data-size="l"
data-slot-names="unnamed"
id="sbb-expansion-panel-header-2"
role="button"
slot="header"
tabindex="0"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ snapshots["sbb-action-group renders renders DOM"] =
data-button=""
data-sbb-button=""
data-slot-names="unnamed"
role="button"
size="l"
tabindex="0"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ snapshots["sbb-alert should render default properties Shadow DOM"] =
data-sbb-button=""
icon-name="cross-small"
negative=""
role="button"
size="m"
tabindex="0"
>
Expand Down Expand Up @@ -158,7 +157,6 @@ snapshots["sbb-alert should render customized properties Shadow DOM"] =
data-sbb-button=""
icon-name="cross-small"
negative=""
role="button"
size="m"
tabindex="0"
>
Expand Down
2 changes: 1 addition & 1 deletion src/elements/button/button-link/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Use the accessibility properties in case of an icon-only button to describe the
| --------------------- | ---------------------- | ------- | -------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `accessibilityLabel` | `accessibility-label` | public | `string` | `''` | This will be forwarded as aria-label to the inner anchor element. |
| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. |
| `disabledInteractive` | `disabled-interactive` | public | `boolean` | `false` | Whether disabled buttons should be interactive. |
| `disabledInteractive` | `disabled-interactive` | public | `boolean` | `false` | Whether the button should be aria-disabled but stay interactive. |
| `download` | `download` | public | `boolean` | `false` | Whether the browser will show the download dialog on click. |
| `href` | `href` | public | `string` | `''` | The href value you want to link to. |
| `iconName` | `icon-name` | public | `string` | `''` | The icon name we want to use, choose from the small icon variants from the ui-icons category from here https://icons.app.sbb.ch. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export const snapshots = {};

snapshots["sbb-button renders a sbb-button without icon DOM"] =
`<sbb-button
aria-disabled="true"
data-action=""
data-button=""
data-sbb-button=""
Expand All @@ -12,7 +11,6 @@ snapshots["sbb-button renders a sbb-button without icon DOM"] =
form="formid"
name="name"
negative=""
role="button"
size="m"
type="button"
value="value"
Expand Down Expand Up @@ -40,7 +38,6 @@ snapshots["sbb-button renders a sbb-button with slotted icon DOM"] =
data-button=""
data-sbb-button=""
data-slot-names="icon unnamed"
role="button"
size="l"
tabindex="0"
>
Expand Down
1 change: 0 additions & 1 deletion src/elements/button/button/button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
} from '../common/common-stories.js';

import readme from './readme.md?raw';
import '../../loading-indicator.js';
import './button.js';

const defaultArgTypes: ArgTypes = { ...buttonDefaultArgTypes };
Expand Down
21 changes: 21 additions & 0 deletions src/elements/button/button/button.visual.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,27 @@ describe(`sbb-button`, () => {
);
});

describe(`disabledInteractive`, () => {
for (const negative of [false, true]) {
describe(`negative=${negative}`, () => {
for (const state of visualDiffStandardStates) {
it(
`${state.name}`,
state.with(async (setup) => {
await setup.withFixture(
html`<sbb-button disabled-interactive ?negative=${negative}>Button</sbb-button>`,
{
backgroundColor: negative ? 'var(--sbb-color-iron)' : undefined,
focusOutlineDark: negative,
},
);
}),
);
}
});
}
});

describe('forcedColors=true', () => {
describeEach(forcedColorCases, ({ disabled, negative }) => {
beforeEach(async function () {
Expand Down
22 changes: 11 additions & 11 deletions src/elements/button/button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,17 @@ guard against such cases in your component.

## Properties

| Name | Attribute | Privacy | Type | Default | Description |
| --------------------- | ---------------------- | ------- | --------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. |
| `disabledInteractive` | `disabled-interactive` | public | `boolean` | `false` | Whether disabled buttons should be interactive. |
| `form` | `form` | public | `string` | `''` | The <form> element to associate the button with. |
| `iconName` | `icon-name` | public | `string` | `''` | The icon name we want to use, choose from the small icon variants from the ui-icons category from here https://icons.app.sbb.ch. |
| `name` | `name` | public | `string` | | The name of the button element. |
| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. |
| `size` | `size` | public | `SbbButtonSize` | `'l'` | Size variant, either l or m. |
| `type` | `type` | public | `SbbButtonType` | `'button'` | The type attribute to use for the button. |
| `value` | `value` | public | `string` | | The value of the button element. |
| Name | Attribute | Privacy | Type | Default | Description |
| --------------------- | ---------------------- | ------- | ------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. |
| `disabledInteractive` | `disabled-interactive` | public | `boolean` | `false` | Whether the button should be aria-disabled but stay interactive. |
| `form` | `form` | public | `HTMLFormElement \| null` | | Returns the form owner of the internals of the target element. |
| `iconName` | `icon-name` | public | `string` | `''` | The icon name we want to use, choose from the small icon variants from the ui-icons category from here https://icons.app.sbb.ch. |
| `name` | `name` | public | `string` | | Name of the form element. Will be read from name attribute. |
| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. |
| `size` | `size` | public | `SbbButtonSize` | `'l'` | Size variant, either l or m. |
| `type` | `type` | public | `SbbButtonType` | `'button'` | The type attribute to use for the button. |
| `value` | `value` | public | `string \| null` | `null` | Value of the form element. |

## Slots

Expand Down
56 changes: 41 additions & 15 deletions src/elements/button/common/button-common-stories.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,49 @@
import type { InputType } from '@storybook/types';
import type { Args, ArgTypes, StoryObj } from '@storybook/web-components';
import type { TemplateResult } from 'lit';
import { nothing, type TemplateResult } from 'lit';
import { html, unsafeStatic } from 'lit/static-html.js';

import { sbbSpread } from '../../../storybook/helpers/spread.js';

import { commonDefaultArgs, commonDefaultArgTypes } from './common-stories.js';

import '../../action-group.js';
import '../../form-field.js';

/* eslint-disable lit/binding-positions, @typescript-eslint/naming-convention */
const RequestSubmitTemplate = ({ tag, text }: Args): TemplateResult => html`
<form id="my-fake-form" action="/submit" method="post" target="_blank">
<label
for="input"
style="display: flex; flex-direction: column; align-items: flex-start; padding-block-end: 2rem;"
>
Input required; submit with empty value is impossible due to 'requestSubmit' API validation.
<input required id="input" />
</label>
<${unsafeStatic(tag)} type="submit" form="my-fake-form" name="input" value="input"> ${text} </${unsafeStatic(tag)}>
</form>
`;
const FormTemplate = ({
tag,
name,
value,
type: _type,
reset: _reset,
...args
}: Args): TemplateResult => html`
<form style="display: flex; gap: 1rem; flex-direction: column;"
@submit=${(e: SubmitEvent) => {
e.preventDefault();
const form = (e.target as HTMLFormElement)!;
form.querySelector('#form-data')!.innerHTML = JSON.stringify(
Object.fromEntries(new FormData(form, e.submitter)),
);
}}>
<p>Input required; submit with empty value is impossible due to 'requestSubmit' API validation.</p>
<sbb-form-field>
<input name="test" value="" required>
</sbb-form-field>
<fieldset>
<sbb-action-group>
<${unsafeStatic(tag)} ${sbbSpread(args)} type="reset">
Reset
</${unsafeStatic(tag)}>
<${unsafeStatic(tag)} ${sbbSpread(args)} value=${value ?? nothing} name=${name ?? nothing} type="submit">
Submit
</${unsafeStatic(tag)}>
</sbb-action-group>
</fieldset>
<div id="form-data"></div>
</form>`;

/* eslint-enable lit/binding-positions, @typescript-eslint/naming-convention */

const type: InputType = {
Expand Down Expand Up @@ -104,6 +130,6 @@ export const buttonDefaultArgs: Args = {
};

export const requestSubmit: StoryObj = {
render: RequestSubmitTemplate,
args: { text: 'Submit form' },
render: FormTemplate,
args: { text: undefined, type: undefined, value: 'submit button' },
};
14 changes: 8 additions & 6 deletions src/elements/button/common/button-common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
@include sbb.box-sizing;

$icon-only: ':where([data-slot-names~=icon], [icon-name]):not([data-slot-names~=unnamed])';
$disabled: '[disabled], :disabled, [disabled-interactive]';
$active: ':active, [data-active]';

:host {
display: inline-block;
Expand Down Expand Up @@ -109,7 +111,7 @@ $icon-only: ':where([data-slot-names~=icon], [icon-name]):not([data-slot-names~=
--sbb-button-padding-inline: 0;
}

:host(:not([disabled], :active, [data-active]):hover) {
:host(:not(#{$disabled}, #{$active}):hover) {
@include sbb.hover-mq($hover: true) {
--sbb-button-translate-y-content-hover: #{sbb.px-to-rem-build(-1)};
--sbb-button-shadow-1-offset-y: calc(
Expand Down Expand Up @@ -166,22 +168,22 @@ $icon-only: ':where([data-slot-names~=icon], [icon-name]):not([data-slot-names~=
transition-property: inset, background-color, border-color, box-shadow;
box-shadow: var(--sbb-button-box-shadow);

:host([disabled]) & {
:host(:is(#{$disabled})) & {
background-color: var(--sbb-button-color-disabled-background);
border-width: var(--sbb-button-border-disabled-width);
border-color: var(--sbb-button-color-disabled-border);
border-style: var(--sbb-button-border-disabled-style);
}

:host(:not([disabled], :active, [data-active]):hover) & {
:host(:not(#{$disabled}, #{$active}):hover) & {
@include sbb.hover-mq($hover: true) {
inset: calc(var(--sbb-button-border-width) * -1);
background-color: var(--sbb-button-color-hover-background);
border-color: var(--sbb-button-color-hover-border);
}
}

:host(:not([disabled]):is(:active, [data-active])) & {
:host(:not(#{$disabled}):is(#{$active})) & {
color: var(--sbb-button-color-active-text);
background-color: var(--sbb-button-color-active-background);
border-color: var(--sbb-button-color-active-border);
Expand All @@ -193,13 +195,13 @@ $icon-only: ':where([data-slot-names~=icon], [icon-name]):not([data-slot-names~=
justify-content: center;
}

:host([disabled]) & {
:host(:is(#{$disabled})) & {
color: var(--sbb-button-color-disabled-text);
cursor: default;
pointer-events: none;
}

:host(:not([disabled], :active, [data-active]):hover) & {
:host(:not(#{$disabled}, #{$active}):hover) & {
@include sbb.hover-mq($hover: true) {
color: var(--sbb-button-color-hover-text);
}
Expand Down
2 changes: 1 addition & 1 deletion src/elements/button/common/primary-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@
--sbb-button-shadow-2-color: var(--sbb-color-metal-alpha-20);
}

:host(:not([disabled], :active, [data-active])) {
:host(:not([disabled], :disabled, [disabled-interactive], :active, [data-active])) {
--sbb-button-box-shadow: var(--sbb-button-box-shadow-definition);
}
2 changes: 1 addition & 1 deletion src/elements/button/common/secondary-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@
--sbb-button-color-hover-text: var(--sbb-color-milk);
}

:host(:not([disabled], [negative], :active, [data-active])) {
:host(:not([disabled], :disabled, [disabled-interactive], [negative], :active, [data-active])) {
--sbb-button-box-shadow: var(--sbb-button-box-shadow-definition);
}
2 changes: 1 addition & 1 deletion src/elements/button/common/tertiary-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
--sbb-button-shadow-2-color: var(--sbb-color-cement-alpha-20);
}

:host(:not([disabled], :active, [data-active])) {
:host(:not([disabled], :disabled, [disabled-interactive], :active, [data-active])) {
--sbb-button-box-shadow: var(--sbb-button-box-shadow-definition);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ snapshots["sbb-mini-button-group renders DOM"] =
data-action=""
data-button=""
icon-name="pen-small"
role="button"
slot="li-0"
tabindex="0"
>
Expand All @@ -26,7 +25,6 @@ snapshots["sbb-mini-button-group renders DOM"] =
data-action=""
data-button=""
icon-name="pen-small"
role="button"
slot="li-2"
tabindex="0"
>
Expand Down Expand Up @@ -70,7 +68,6 @@ snapshots["sbb-mini-button-group renders negative DOM"] =
data-button=""
icon-name="pen-small"
negative=""
role="button"
slot="li-0"
tabindex="0"
>
Expand All @@ -80,7 +77,6 @@ snapshots["sbb-mini-button-group renders negative DOM"] =
data-button=""
icon-name="pen-small"
negative=""
role="button"
slot="li-1"
tabindex="0"
>
Expand Down
4 changes: 3 additions & 1 deletion src/elements/button/mini-button/mini-button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import type {
import { html, type TemplateResult } from 'lit';

import { sbbSpread } from '../../../storybook/helpers/spread.js';
import { buttonDefaultArgs, buttonDefaultArgTypes } from '../common/button-common-stories.js';

import '../../form-field.js';
import '../../icon.js';
import './mini-button.js';
import { buttonDefaultArgs, buttonDefaultArgTypes } from '../common/button-common-stories.js';

import readme from './readme.md?raw';

Expand Down
Loading

0 comments on commit eeb7a0a

Please sign in to comment.