diff --git a/.changeset/config.json b/.changeset/config.json index 6e84596498..fd51e1bc7d 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -21,6 +21,7 @@ "html-test-app", "ionic-test-app", "figma-plugin", - "documentation" + "documentation", + "storybook-docs" ] } diff --git a/.changeset/cyan-mugs-travel.md b/.changeset/cyan-mugs-travel.md new file mode 100644 index 0000000000..f4a19667b2 --- /dev/null +++ b/.changeset/cyan-mugs-travel.md @@ -0,0 +1,5 @@ +--- +'@siemens/ix': patch +--- + +Adjust the spacing of the toggle diff --git a/.changeset/fresh-suns-cry.md b/.changeset/fresh-suns-cry.md new file mode 100644 index 0000000000..6b9bc7077d --- /dev/null +++ b/.changeset/fresh-suns-cry.md @@ -0,0 +1,5 @@ +--- +'@siemens/ix': patch +--- + +Align label overflow behavior of **ix-toggle** to wrap diff --git a/.changeset/small-games-act.md b/.changeset/small-games-act.md new file mode 100644 index 0000000000..70cf0f5ab6 --- /dev/null +++ b/.changeset/small-games-act.md @@ -0,0 +1,5 @@ +--- +'@siemens/ix': patch +--- + +Increase gap between switch and label of **ix-toggle** diff --git a/.changeset/thin-frogs-relax.md b/.changeset/thin-frogs-relax.md new file mode 100644 index 0000000000..d3e9bfe348 --- /dev/null +++ b/.changeset/thin-frogs-relax.md @@ -0,0 +1,5 @@ +--- +'@siemens/ix': patch +--- + +Make **ix-toggle** not clickable if disabled property is present (Fixes #1585) diff --git a/packages/core/component-doc.json b/packages/core/component-doc.json index dc886825fa..ada4ed96bf 100644 --- a/packages/core/component-doc.json +++ b/packages/core/component-doc.json @@ -19600,8 +19600,14 @@ ], "encapsulation": "shadow", "dependents": [], - "dependencies": [], - "dependencyGraph": {}, + "dependencies": [ + "ix-typography" + ], + "dependencyGraph": { + "ix-toggle": [ + "ix-typography" + ] + }, "props": [ { "name": "checked", @@ -20635,6 +20641,7 @@ "ix-slider", "ix-textarea", "ix-time-picker", + "ix-toggle", "ix-tooltip" ], "dependencies": [], @@ -20711,6 +20718,9 @@ "ix-time-picker": [ "ix-typography" ], + "ix-toggle": [ + "ix-typography" + ], "ix-tooltip": [ "ix-typography" ] diff --git a/packages/core/src/components/toggle/toggle.scss b/packages/core/src/components/toggle/toggle.scss index 1618ef8d03..f71888870c 100644 --- a/packages/core/src/components/toggle/toggle.scss +++ b/packages/core/src/components/toggle/toggle.scss @@ -13,33 +13,44 @@ :host { @include ix-component; display: inline-flex; + flex-direction: row; position: relative; - height: 2rem; - justify-content: flex-start; - align-items: center; - margin-right: 0.25rem; + height: 1.5rem; + margin-block-start: 0.25rem; + margin-block-end: 0.25rem; - cursor: pointer; + input[type='checkbox'] { + display: none; + } + + .wrapper { + display: flex; + align-items: flex-start; + width: 100%; + height: 100%; + } + + .label { + display: inline-block; + white-space: normal; + overflow: hidden; + text-overflow: ellipsis; + margin-block-start: 0.125rem; + margin-inline-start: 0.5625rem; + } .switch { + all: unset; position: relative; display: inline-block; width: 3rem; min-width: 3rem; max-width: 3rem; height: 1.5rem; - margin-right: 0.25rem; - } - - .switch input { - opacity: 0; - width: 0; - height: 0; } .slider { position: absolute; - cursor: pointer; top: 0; left: 0; right: 0; @@ -47,7 +58,7 @@ background-color: var(--theme-switch-off--background); transition: var(--theme-default-time); border-radius: 1.5rem; - border: 1px solid transparent; + border: 0.0625rem solid transparent; } .slider:before { @@ -55,116 +66,103 @@ content: ''; height: 1.125rem; width: 1.125rem; - left: 4px; - bottom: 2px; + left: 0.125rem; + bottom: 0.125rem; background-color: var(--theme-switch-thumb-off--background); transition: var(--theme-default-time); border-radius: 50%; } - input { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; - } - - input:focus-visible + .switch > .slider { - outline: 1px solid var(--theme-color-focus-bdr); - outline-offset: 1px; - } - - input:checked + .switch > .slider::before { + .switch.checked > .slider::before { background-color: var(--theme-switch-thumb-on--background); - transform: translateX(1.35rem); + transform: translateX(1.5rem); } - // Toggle NOT checked - input + .switch > .slider { + .switch > .slider { border-color: var(--theme-switch-off--border-color); } - input + .switch:hover > .slider { + .switch:hover > .slider { background-color: var(--theme-switch-off--background--hover); border-color: var(--theme-switch-off--border-color--hover); } - input + .switch:hover > .slider:before { + .switch:hover > .slider:before { background-color: var(--theme-switch-thumb-off--background--hover); } - input + .switch:active > .slider { + .switch:active > .slider { background-color: var(--theme-switch-off--background--active); border-color: var(--theme-switch-off--border-color--active); } - input + .switch:active > .slider:before { + .switch:active > .slider:before { background-color: var(--theme-switch-thumb-off--background--active); } - // Toggle checked - input:checked + .switch > .slider { + .switch.checked > .slider { background-color: var(--theme-switch-on--background); border-color: var(--theme-switch-on--border-color); } - input:checked + .switch:hover > .slider { + .switch.checked:hover > .slider { background-color: var(--theme-switch-on--background--hover); border-color: var(--theme-switch-on--border-color--hover); } - input:checked + .switch:hover > .slider:before { + .switch.checked:hover > .slider:before { background-color: var(--theme-switch-thumb-on--background--hover); } - input:checked + .switch:active > .slider { + .switch.checked:active > .slider { background-color: var(--theme-switch-on--background--active); border-color: var(--theme-switch-on--border-color--active); } - input:checked + .switch:active > .slider:before { + .switch.checked:active > .slider:before { background-color: var(--theme-switch-thumb-on--background--active); } - input:indeterminate + .switch > .slider::before { - transform: translateX(0.7rem); + .switch.indeterminate > .slider::before { + transform: translateX(0.75rem); } +} - .toggle-text { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; +:host(:not(.disabled)) { + .wrapper { + cursor: pointer; + } + + .switch:focus-visible > .slider { + outline: 0.0625rem solid var(--theme-color-focus-bdr); + outline-offset: 0.0625rem; } } :host(.disabled) { pointer-events: none; - input + .switch { + .switch { opacity: 0.5; } - input + .switch > .slider { + .switch > .slider { background-color: var(--theme-switch-off--background--disabled); } - input + .switch > .slider:before { + .switch > .slider:before { background-color: var(--theme-switch-thumb-off--background--disabled); } - input:checked + .switch > .slider { + .switch.checked > .slider { background-color: var(--theme-switch-on--background--disabled); } - input:checked + .switch > .slider:before { + .switch.checked > .slider:before { background-color: var(--theme-switch-thumb-on--background--disabled); } - .toggle-text { + .label { color: var(--theme-color-weak-text); } } @@ -178,46 +176,56 @@ } @mixin define-toggle-vars($state, $type) { - --theme-switch-#{$state}--background: var(--theme-switch-#{$state}--background--#{$type}); - --theme-switch-#{$state}--background--hover: var(--theme-switch-#{$state}--background--#{$type}--hover); - --theme-switch-#{$state}--background--active: var(--theme-switch-#{$state}--background--#{$type}--active); - - --theme-switch-thumb-#{$state}--background: var(--theme-switch-thumb-#{$state}--background--#{$type}); - --theme-switch-thumb-#{$state}--background--hover: var(--theme-switch-thumb-#{$state}--background--#{$type}--hover); - --theme-switch-thumb-#{$state}--background--active: var(--theme-switch-thumb-#{$state}--background--#{$type}--active); - - --theme-switch-#{$state}--border-color: var(--theme-switch-#{$state}--border-color--#{$type}); - --theme-switch-#{$state}--border-color--hover: var(--theme-switch-#{$state}--border-color--#{$type}--hover); - --theme-switch-#{$state}--border-color--active: var(--theme-switch-#{$state}--border-color--#{$type}--active); + --theme-switch-#{$state}--background: var( + --theme-switch-#{$state}--background--#{$type} + ); + --theme-switch-#{$state}--background--hover: var( + --theme-switch-#{$state}--background--#{$type}--hover + ); + --theme-switch-#{$state}--background--active: var( + --theme-switch-#{$state}--background--#{$type}--active + ); + + --theme-switch-thumb-#{$state}--background: var( + --theme-switch-thumb-#{$state}--background--#{$type} + ); + --theme-switch-thumb-#{$state}--background--hover: var( + --theme-switch-thumb-#{$state}--background--#{$type}--hover + ); + --theme-switch-thumb-#{$state}--background--active: var( + --theme-switch-thumb-#{$state}--background--#{$type}--active + ); + + --theme-switch-#{$state}--border-color: var( + --theme-switch-#{$state}--border-color--#{$type} + ); + --theme-switch-#{$state}--border-color--hover: var( + --theme-switch-#{$state}--border-color--#{$type}--hover + ); + --theme-switch-#{$state}--border-color--active: var( + --theme-switch-#{$state}--border-color--#{$type}--active + ); } -@include toggle-variant( - $selector: '.ix-info', -) { +@include toggle-variant($selector: '.ix-info') { @include define-toggle-vars('off', 'info'); @include define-toggle-vars('on', 'info'); @include define-toggle-vars('mixed', 'info'); } -@include toggle-variant( - $selector: '.ix-warning', -) { +@include toggle-variant($selector: '.ix-warning') { @include define-toggle-vars('off', 'warning'); @include define-toggle-vars('on', 'warning'); @include define-toggle-vars('mixed', 'warning'); } -@include toggle-variant( - $selector: '.ix-invalid--required', -) { +@include toggle-variant($selector: '.ix-invalid--required') { @include define-toggle-vars('off', 'invalid'); @include define-toggle-vars('on', 'invalid'); @include define-toggle-vars('mixed', 'invalid'); } -@include toggle-variant( - $selector: '.ix-invalid', -) { +@include toggle-variant($selector: '.ix-invalid') { @include define-toggle-vars('off', 'invalid'); @include define-toggle-vars('on', 'invalid'); @include define-toggle-vars('mixed', 'invalid'); diff --git a/packages/core/src/components/toggle/toggle.tsx b/packages/core/src/components/toggle/toggle.tsx index 04cb8035c6..450f09b985 100644 --- a/packages/core/src/components/toggle/toggle.tsx +++ b/packages/core/src/components/toggle/toggle.tsx @@ -13,7 +13,6 @@ import { Element, Event, EventEmitter, - Fragment, h, Host, Method, @@ -98,6 +97,10 @@ export class Toggle implements IxFormComponent { @Event() valueChange!: EventEmitter; onCheckedChange(newChecked: boolean) { + if (this.disabled) { + return; + } + if (this.indeterminate) { this.indeterminate = false; } @@ -135,41 +138,50 @@ export class Toggle implements IxFormComponent { } render() { + let toggleText = this.textOff; + + if (this.checked) { + toggleText = this.textOn; + } + + if (this.indeterminate) { + toggleText = this.textIndeterminate; + } return ( this.onCheckedChange(!this.checked)} > - - this.onCheckedChange((event.target as HTMLInputElement).checked) - } - > - ); } diff --git a/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-basic-1-chromium---theme-classic-dark-linux.png b/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-basic-1-chromium---theme-classic-dark-linux.png index 134ae91640..9c5abb662d 100644 Binary files a/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-basic-1-chromium---theme-classic-dark-linux.png and b/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-basic-1-chromium---theme-classic-dark-linux.png differ diff --git a/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-basic-1-chromium---theme-classic-light-linux.png b/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-basic-1-chromium---theme-classic-light-linux.png index bb165eb398..6ea1f1e2a8 100644 Binary files a/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-basic-1-chromium---theme-classic-light-linux.png and b/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-basic-1-chromium---theme-classic-light-linux.png differ diff --git a/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-disabled-1-chromium---theme-classic-dark-linux.png b/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-disabled-1-chromium---theme-classic-dark-linux.png index 617f4fd770..08738244d8 100644 Binary files a/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-disabled-1-chromium---theme-classic-dark-linux.png and b/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-disabled-1-chromium---theme-classic-dark-linux.png differ diff --git a/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-disabled-1-chromium---theme-classic-light-linux.png b/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-disabled-1-chromium---theme-classic-light-linux.png index 7b34f6fafc..5e73d78dd5 100644 Binary files a/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-disabled-1-chromium---theme-classic-light-linux.png and b/packages/core/src/tests/toggle/toggle.e2e.ts-snapshots/toggle-disabled-1-chromium---theme-classic-light-linux.png differ diff --git a/packages/core/stencil.config.ts b/packages/core/stencil.config.ts index 189e297c3b..cbe4ac2fab 100644 --- a/packages/core/stencil.config.ts +++ b/packages/core/stencil.config.ts @@ -30,7 +30,7 @@ export const config: Config = { browserHeadless: 'new', }, namespace: 'siemens-ix', - watchIgnoredRegex: [/scss/, /component-doc.json/], + watchIgnoredRegex: [/component-doc.json/], globalStyle: './scss/ix.scss', minifyCss: false, plugins: [ diff --git a/packages/storybook-docs/package.json b/packages/storybook-docs/package.json index e266a3a2d8..5d937e2d62 100644 --- a/packages/storybook-docs/package.json +++ b/packages/storybook-docs/package.json @@ -18,6 +18,7 @@ "@siemens/ix": "workspace:*", "@siemens/ix-icons": "^2.2.0", "@storybook/addon-a11y": "^8.4.2", + "@storybook/addon-actions": "^8.4.2", "@storybook/addon-designs": "^8.0.4", "@storybook/addon-essentials": "^8.4.2", "@storybook/addon-interactions": "^8.4.2", diff --git a/packages/storybook-docs/src/stories/toggle.stories.ts b/packages/storybook-docs/src/stories/toggle.stories.ts new file mode 100644 index 0000000000..d4363de806 --- /dev/null +++ b/packages/storybook-docs/src/stories/toggle.stories.ts @@ -0,0 +1,104 @@ +/* + * SPDX-FileCopyrightText: 2024 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import type { ArgTypes, Meta, StoryObj } from '@storybook/web-components'; +import type { Components } from '@siemens/ix/components'; +import { genericRender, makeArgTypes } from './utils/generic-render'; +import { action } from '@storybook/addon-actions'; + +type Element = Components.IxToggle & { + defaultSlot: string; + ['checked-change']: any; + validation: string; + 'text-on': string; +}; + +const toggleRender = (args: Element) => { + const container = genericRender('ix-toggle', args); + const ixToggle = container.querySelector('ix-toggle') as HTMLIxToggleElement; + ixToggle.addEventListener('checkedChange', action('checkedChange')); + ixToggle.classList.remove('ix-invalid', 'ix-valid', 'ix-warning', 'ix-info'); + ixToggle.classList.add(args.validation); + return container; +}; + +const meta = { + title: 'Example/Toggle', + tags: [], + render: (args) => toggleRender(args), + argTypes: makeArgTypes>>('ix-toggle', { + validation: { + control: { type: 'select' }, + options: ['ix-invalid', 'ix-valid', 'ix-warning', 'ix-info'], + }, + }), + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/design/r2nqdNNXXZtPmWuVjIlM1Q/iX-Components---Brand-Dark?node-id=225-5535&m=dev', + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Default: Story = { + args: { + disabled: false, + }, +}; + +export const Valid: Story = { + args: { + checked: true, + disabled: false, + validation: 'ix-valid', + }, +}; + +export const Invalid: Story = { + args: { + checked: true, + disabled: false, + validation: 'ix-invalid', + }, +}; + +export const Info: Story = { + args: { + checked: true, + disabled: false, + validation: 'ix-info', + }, +}; + +export const Warning: Story = { + args: { + checked: true, + disabled: false, + validation: 'ix-warning', + }, +}; + +export const Overflow: Story = { + args: { + disabled: false, + 'text-on': 'Lorem ipsum dolor sit amet consectetur adipisicing elit', + checked: true, + }, + render: (args) => { + const container = toggleRender(args); + const ixToggle = container.querySelector('ix-toggle')!; + ixToggle.style.width = '10rem'; + ixToggle.style.height = '3rem'; + + return container; + }, +}; diff --git a/packages/storybook-docs/src/stories/utils/generic-render.ts b/packages/storybook-docs/src/stories/utils/generic-render.ts index 4ef2ee919f..a8de5c7964 100644 --- a/packages/storybook-docs/src/stories/utils/generic-render.ts +++ b/packages/storybook-docs/src/stories/utils/generic-render.ts @@ -61,12 +61,18 @@ export function makeArgTypes( return !ignore.includes(prop.name); }) .forEach((prop) => { + let attributeName = prop.name; + + if ('attr' in prop && prop.attr) { + attributeName = prop.attr; + } + if ( prop.values.length > 1 && prop.values.every((value) => value.type === 'string') ) { - argTypes[prop.name] = { - name: `${prop.name}*`, + argTypes[attributeName] = { + name: `${attributeName}*`, control: { type: 'select' }, options: (prop.values as { type: 'string'; value: string }[]) .filter((v) => v.value !== '') @@ -76,8 +82,8 @@ export function makeArgTypes( return; } - if (prop.name.includes('icon')) { - argTypes[prop.name] = { + if (attributeName.includes('icon')) { + argTypes[attributeName] = { control: { type: 'select' }, options: icons, }; @@ -85,7 +91,7 @@ export function makeArgTypes( return; } - argTypes[prop.name] = { + argTypes[attributeName] = { control: switchTypes(prop.type), }; }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e291c7c8cc..a968e5c621 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -956,6 +956,9 @@ importers: '@storybook/addon-a11y': specifier: ^8.4.2 version: 8.4.2(storybook@8.4.2(prettier@3.0.3)) + '@storybook/addon-actions': + specifier: ^8.4.2 + version: 8.4.2(storybook@8.4.2(prettier@3.0.3)) '@storybook/addon-designs': specifier: ^8.0.4 version: 8.0.4(@storybook/blocks@8.4.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.4.2(prettier@3.0.3)))(@storybook/components@8.4.2(storybook@8.4.2(prettier@3.0.3)))(@storybook/theming@8.4.2(storybook@8.4.2(prettier@3.0.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)