From 4fc8dc45e7344de0751b0dfd93a892b70ee8a8d1 Mon Sep 17 00:00:00 2001 From: Giovanna Monti Date: Tue, 29 Oct 2024 17:23:28 +0100 Subject: [PATCH 01/13] add: switch component with basic functionality --- src/components/Switch/Switch.props.ts | 61 +++++++++ src/components/Switch/Switch.test.tsx | 75 +++++++++++ src/components/Switch/Switch.tsx | 57 +++++++++ src/components/Switch/Switch.types.ts | 22 ++++ .../Switch/__snapshots__/Switch.test.tsx.snap | 119 ++++++++++++++++++ src/components/Switch/index.ts | 19 +++ src/index.ts | 2 + 7 files changed, 355 insertions(+) create mode 100644 src/components/Switch/Switch.props.ts create mode 100644 src/components/Switch/Switch.test.tsx create mode 100644 src/components/Switch/Switch.tsx create mode 100644 src/components/Switch/Switch.types.ts create mode 100644 src/components/Switch/__snapshots__/Switch.test.tsx.snap create mode 100644 src/components/Switch/index.ts diff --git a/src/components/Switch/Switch.props.ts b/src/components/Switch/Switch.props.ts new file mode 100644 index 00000000..8c618513 --- /dev/null +++ b/src/components/Switch/Switch.props.ts @@ -0,0 +1,61 @@ +/** + * Copyright 2024 Mia srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SwitchChangeEventHandler, SwitchClickEventHandler } from 'antd/es/switch' + +import { Size } from './Switch.types' + +export type SwitchProps = { + + /** + * Determines whether the switch is checked. + */ + isChecked?: boolean, + + /** + * Determines whether the switch is disabled. Defaults to false. + */ + isDisabled?: boolean, + + /** + * Determines whether the switch is checked on its first render. + * It is overridden by the isChecked prop, when present. + * Defaults to false. + */ + isInitiallyChecked?: boolean, + + /** + * Determines whether the switch is in loading state. Defaults to false. + */ + isLoading?: boolean, + + /** + * Function that is invoked when the switch state is changed. + */ + onChange?: SwitchChangeEventHandler + + /** + * Function that is invoked when the switch is clicked. + */ + onClick?: SwitchClickEventHandler, + + /** + * Determines the switch size. Can be set to `large` or `small`. Defaults to `large`. + */ + size?: Size, +} diff --git a/src/components/Switch/Switch.test.tsx b/src/components/Switch/Switch.test.tsx new file mode 100644 index 00000000..4e3dbad1 --- /dev/null +++ b/src/components/Switch/Switch.test.tsx @@ -0,0 +1,75 @@ +import { render, screen, userEvent, waitFor } from '../../test-utils' +import { Size } from './Switch.types' +import { Switch } from './Switch' + +describe('Switch', () => { + it('renders switch in default state', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).not.toBeChecked() + }) + + it('renders switch checked by default', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeChecked() + }) + + it('renders checked switch', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeChecked() + }) + + it('renders disabled switch', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeDisabled() + }) + + it('renders disabled checked switch', () => { + const { asFragment } = render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeDisabled() + expect(screen.getByRole('switch')).toBeChecked() + expect(asFragment()).toMatchSnapshot() + }) + + it('renders unchecked loading switch', () => { + const { asFragment } = render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeDisabled() + expect(asFragment()).toMatchSnapshot() + }) + + it('renders checked loading switch', () => { + const { asFragment } = render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeDisabled() + expect(asFragment()).toMatchSnapshot() + }) + + it('renders small size switch', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toHaveClass('mia-platform-switch-small') + }) + + it('calls onClick when the switch is clicked', async() => { + const onClick = jest.fn() + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).not.toBeChecked() + userEvent.click(screen.getByRole('switch')) + await waitFor(() => expect(onClick).toHaveBeenCalledTimes(1)) + }) + + it('calls onChange when the switch state changes', async() => { + const onChange = jest.fn() + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).not.toBeChecked() + userEvent.click(screen.getByRole('switch')) + await waitFor(() => expect(onChange).toHaveBeenCalledTimes(1)) + }) +}) diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx new file mode 100644 index 00000000..dfd098e1 --- /dev/null +++ b/src/components/Switch/Switch.tsx @@ -0,0 +1,57 @@ +/** + * Copyright 2024 Mia srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Switch as AntSwitch } from 'antd' +import { ReactElement } from 'react' +import type { SwitchSize } from 'antd/es/switch' + +import { Size } from './Switch.types' +import { SwitchProps } from './Switch.props' + +export const defaults = { + isDisabled: false, + isInitiallyChecked: false, + isLoading: false, + size: Size.Large, +} + +const antSizeRemapping = { + [Size.Large]: 'default' as SwitchSize, + [Size.Small]: 'small' as SwitchSize, +} + +export const Switch = ({ + isChecked, + isInitiallyChecked = defaults.isInitiallyChecked, + isDisabled = defaults.isDisabled, + isLoading = defaults.isLoading, + onChange, + onClick, + size = defaults.size, +} : SwitchProps) : ReactElement => { + return ( + ) +} diff --git a/src/components/Switch/Switch.types.ts b/src/components/Switch/Switch.types.ts new file mode 100644 index 00000000..860851f6 --- /dev/null +++ b/src/components/Switch/Switch.types.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2023 Mia srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export enum Size { + Small = 'small', + Large = 'large', + } diff --git a/src/components/Switch/__snapshots__/Switch.test.tsx.snap b/src/components/Switch/__snapshots__/Switch.test.tsx.snap new file mode 100644 index 00000000..180c1c5a --- /dev/null +++ b/src/components/Switch/__snapshots__/Switch.test.tsx.snap @@ -0,0 +1,119 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Switch renders checked loading switch 1`] = ` + + + +`; + +exports[`Switch renders disabled checked switch 1`] = ` + + + +`; + +exports[`Switch renders unchecked loading switch 1`] = ` + + + +`; diff --git a/src/components/Switch/index.ts b/src/components/Switch/index.ts new file mode 100644 index 00000000..4094c6fb --- /dev/null +++ b/src/components/Switch/index.ts @@ -0,0 +1,19 @@ +/** + * Copyright 2024 Mia srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export { Switch } from './Switch' diff --git a/src/index.ts b/src/index.ts index 840eadce..a086e019 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,6 +29,7 @@ import { InputNumber } from './components/InputNumber' import { Menu } from './components/Menu' import { Modal } from './components/Modal' import { SegmentedControl } from './components/SegmentedControl' +import { Switch } from './components/Switch' import { Table } from './components/Table' import { Tag } from './components/Tag' import { TextArea } from './components/TextArea/TextArea' @@ -52,6 +53,7 @@ export { Menu, Modal, SegmentedControl, + Switch, Table, Tag, TextArea, From 978b13c2bf4165901ae7553f9bcfabee4700d1e0 Mon Sep 17 00:00:00 2001 From: Giovanna Monti Date: Tue, 29 Oct 2024 17:29:19 +0100 Subject: [PATCH 02/13] refactor: test structure --- src/components/Switch/Switch.test.tsx | 128 ++++++++++-------- .../Switch/__snapshots__/Switch.test.tsx.snap | 6 +- 2 files changed, 74 insertions(+), 60 deletions(-) diff --git a/src/components/Switch/Switch.test.tsx b/src/components/Switch/Switch.test.tsx index 4e3dbad1..9d7ae1f3 100644 --- a/src/components/Switch/Switch.test.tsx +++ b/src/components/Switch/Switch.test.tsx @@ -3,73 +3,87 @@ import { Size } from './Switch.types' import { Switch } from './Switch' describe('Switch', () => { - it('renders switch in default state', () => { - render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).not.toBeChecked() - }) + describe('renders states correctly', () => { + it('renders default state', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).not.toBeChecked() + }) - it('renders switch checked by default', () => { - render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).toBeChecked() - }) + it('renders checked by default state', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeChecked() + }) - it('renders checked switch', () => { - render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).toBeChecked() - }) + it('renders checked state', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeChecked() + }) - it('renders disabled switch', () => { - render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).toBeDisabled() - }) + it('renders disabled state', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeDisabled() + }) - it('renders disabled checked switch', () => { - const { asFragment } = render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).toBeDisabled() - expect(screen.getByRole('switch')).toBeChecked() - expect(asFragment()).toMatchSnapshot() - }) + it('renders disabled checked state', () => { + const { asFragment } = render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeDisabled() + expect(screen.getByRole('switch')).toBeChecked() + expect(asFragment()).toMatchSnapshot() + }) - it('renders unchecked loading switch', () => { - const { asFragment } = render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).toBeDisabled() - expect(asFragment()).toMatchSnapshot() - }) + it('renders unchecked loading state', () => { + const { asFragment } = render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeDisabled() + expect(asFragment()).toMatchSnapshot() + }) - it('renders checked loading switch', () => { - const { asFragment } = render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).toBeDisabled() - expect(asFragment()).toMatchSnapshot() + it('renders checked loading state', () => { + const { asFragment } = render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toBeDisabled() + expect(asFragment()).toMatchSnapshot() + }) }) - it('renders small size switch', () => { - render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).toHaveClass('mia-platform-switch-small') - }) + describe('renders sizes correctly', () => { + it('renders large size', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toHaveClass('mia-platform-switch') + expect(screen.getByRole('switch')).not.toHaveClass('mia-platform-switch-small') + }) - it('calls onClick when the switch is clicked', async() => { - const onClick = jest.fn() - render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).not.toBeChecked() - userEvent.click(screen.getByRole('switch')) - await waitFor(() => expect(onClick).toHaveBeenCalledTimes(1)) + it('renders small size', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).toHaveClass('mia-platform-switch') + expect(screen.getByRole('switch')).toHaveClass('mia-platform-switch-small') + }) }) - it('calls onChange when the switch state changes', async() => { - const onChange = jest.fn() - render() - expect(screen.getByRole('switch')).toBeInTheDocument() - expect(screen.getByRole('switch')).not.toBeChecked() - userEvent.click(screen.getByRole('switch')) - await waitFor(() => expect(onChange).toHaveBeenCalledTimes(1)) + describe('performs interactions correctly', () => { + it('calls onClick when the switch is clicked', async() => { + const onClick = jest.fn() + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).not.toBeChecked() + userEvent.click(screen.getByRole('switch')) + await waitFor(() => expect(onClick).toHaveBeenCalledTimes(1)) + }) + + it('calls onChange when the switch state changes', async() => { + const onChange = jest.fn() + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch')).not.toBeChecked() + userEvent.click(screen.getByRole('switch')) + await waitFor(() => expect(onChange).toHaveBeenCalledTimes(1)) + }) }) }) diff --git a/src/components/Switch/__snapshots__/Switch.test.tsx.snap b/src/components/Switch/__snapshots__/Switch.test.tsx.snap index 180c1c5a..3418fa57 100644 --- a/src/components/Switch/__snapshots__/Switch.test.tsx.snap +++ b/src/components/Switch/__snapshots__/Switch.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Switch renders checked loading switch 1`] = ` +exports[`Switch renders states correctly renders checked loading state 1`] = ` + class="mia-platform-switch-inner" + > + + + + + `; exports[`Switch renders states correctly renders disabled checked state 1`] = ` - + class="mia-platform-switch-inner" + > + + + + + `; exports[`Switch renders states correctly renders unchecked loading state 1`] = ` - + class="mia-platform-switch-inner" + > + + + + + `; From 8f265a8e6d5e0d9a35c956fae51118911d155068 Mon Sep 17 00:00:00 2001 From: Giovanna Monti Date: Tue, 29 Oct 2024 18:25:20 +0100 Subject: [PATCH 04/13] add: switch description --- src/components/Switch/Switch.module.css | 21 +- src/components/Switch/Switch.props.ts | 10 +- src/components/Switch/Switch.stories.tsx | 3 + src/components/Switch/Switch.test.tsx | 13 ++ src/components/Switch/Switch.tsx | 44 ++-- .../Switch/__snapshots__/Switch.test.tsx.snap | 190 ++++++++++-------- 6 files changed, 174 insertions(+), 107 deletions(-) diff --git a/src/components/Switch/Switch.module.css b/src/components/Switch/Switch.module.css index 49b5dede..6239882d 100644 --- a/src/components/Switch/Switch.module.css +++ b/src/components/Switch/Switch.module.css @@ -1,5 +1,20 @@ .switchWrapper { display: flex; - align-items: center; - gap: var(--spacing-gap-md, 12px); -} \ No newline at end of file + flex-direction: column; + gap: var(--spacing-gap-xs, 4px); + + .switchTextWrapper { + display: flex; + align-items: center; + gap: var(--spacing-gap-md, 12px); + } + + .switchDescription { + padding-right: var(--spacing-padding-sm, 8px); + padding-left: calc(var(--spacing-padding-sm, 8px) + var(--spacing-padding-3xl, 48px)); + + &.small { + padding-left: calc(var(--spacing-padding-sm, 8px) + var(--spacing-padding-2xl, 32px)); + } + } +} diff --git a/src/components/Switch/Switch.props.ts b/src/components/Switch/Switch.props.ts index cc6f7960..5d621e05 100644 --- a/src/components/Switch/Switch.props.ts +++ b/src/components/Switch/Switch.props.ts @@ -23,6 +23,12 @@ import { Size } from './Switch.types' export type SwitchProps = { + /** + * Additional description of the switch, to be rendered below the switch `text`. + * Is ignored if the `text` prop is not provided. + */ + description?: ReactNode, + /** * Determines whether the switch is checked. */ @@ -34,9 +40,7 @@ export type SwitchProps = { isDisabled?: boolean, /** - * Determines whether the switch is checked on its first render. - * It is overridden by the isChecked prop, when present. - * Defaults to false. + * Determines whether the switch is in loading state. Defaults to false. */ isInitiallyChecked?: boolean, diff --git a/src/components/Switch/Switch.stories.tsx b/src/components/Switch/Switch.stories.tsx index e92e04cd..50419eb0 100644 --- a/src/components/Switch/Switch.stories.tsx +++ b/src/components/Switch/Switch.stories.tsx @@ -25,9 +25,12 @@ const meta = { component: Switch, args: { ...defaults, + description: 'Some description', text: 'Some text', }, argTypes: { + description: { type: 'string' }, + text: { type: 'string' }, onClick: { control: false }, onChange: { control: false }, }, diff --git a/src/components/Switch/Switch.test.tsx b/src/components/Switch/Switch.test.tsx index 2f982dbe..1112de77 100644 --- a/src/components/Switch/Switch.test.tsx +++ b/src/components/Switch/Switch.test.tsx @@ -73,6 +73,19 @@ describe('Switch', () => { expect(screen.getByRole('switch')).toBeInTheDocument() expect(screen.getByText('Switch')).toBeInTheDocument() }) + + it('renders text and description', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByText('Switch text')).toBeInTheDocument() + expect(screen.getByText('Switch description')).toBeInTheDocument() + }) + + it('ignores description if text is not set', () => { + render() + expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.queryByText('Switch description')).not.toBeInTheDocument() + }) }) describe('performs interactions correctly', () => { diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index 0e0e06d8..3ac1ee91 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -16,9 +16,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { ReactElement, useMemo } from 'react' import { Switch as AntSwitch } from 'antd' -import { ReactElement } from 'react' import type { SwitchSize } from 'antd/es/switch' +import classNames from 'classnames' import { BodyS } from '../Typography/BodyX/BodyS' import { Size } from './Switch.types' @@ -27,6 +28,9 @@ import styles from './Switch.module.css' const { switchWrapper, + switchTextWrapper, + switchDescription, + small: smallSwitch, } = styles export const defaults = { @@ -41,7 +45,10 @@ const antSizeRemapping = { [Size.Small]: 'small' as SwitchSize, } +// TODO: adapt tokens for different states (disabled, etc.) + export const Switch = ({ + description, isChecked, isInitiallyChecked = defaults.isInitiallyChecked, isDisabled = defaults.isDisabled, @@ -51,19 +58,32 @@ export const Switch = ({ size = defaults.size, text, } : SwitchProps) : ReactElement => { + const descriptionClassName = useMemo(() => classNames([ + switchDescription, + size === Size.Small && smallSwitch, + ]), [size]) + return (
- +
+ + {text && {text}} +
{ - text && {text} + text && description && ( +
+ {description} +
+ ) } -
) + + ) } diff --git a/src/components/Switch/__snapshots__/Switch.test.tsx.snap b/src/components/Switch/__snapshots__/Switch.test.tsx.snap index c2ab3f3c..1cc8814b 100644 --- a/src/components/Switch/__snapshots__/Switch.test.tsx.snap +++ b/src/components/Switch/__snapshots__/Switch.test.tsx.snap @@ -5,47 +5,51 @@ exports[`Switch renders states correctly renders checked loading state 1`] = `
-
- - + class="mia-platform-switch-inner" + > + + + + + `; @@ -55,27 +59,31 @@ exports[`Switch renders states correctly renders disabled checked state 1`] = `
- + class="mia-platform-switch-inner" + > + + + + +
`; @@ -85,47 +93,51 @@ exports[`Switch renders states correctly renders unchecked loading state 1`] = `
-
- - - + class="mia-platform-switch-inner" + > + + + + + `; From ec22c76331f2e5784c7c9265ab249065e34ea519 Mon Sep 17 00:00:00 2001 From: Giovanna Monti Date: Wed, 30 Oct 2024 16:56:08 +0100 Subject: [PATCH 05/13] add: theme overrides --- src/components/Switch/Switch.theme.ts | 33 +++++++++++++++++++ src/components/ThemeProvider/Ant.tsx | 2 ++ .../__tests__/__snapshots__/Ant.test.tsx.snap | 12 +++++++ 3 files changed, 47 insertions(+) create mode 100644 src/components/Switch/Switch.theme.ts diff --git a/src/components/Switch/Switch.theme.ts b/src/components/Switch/Switch.theme.ts new file mode 100644 index 00000000..3c1611dd --- /dev/null +++ b/src/components/Switch/Switch.theme.ts @@ -0,0 +1,33 @@ +/** + * Copyright 2024 Mia srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ComponentsTheme } from '../ThemeProvider/Ant' +import Theme from '../../themes/schema' + +/** + * Generates a Ant theme configuration for Switch component based on a theme configuration. + * + * @link https://ant.design/components/switch#design-token + * + * @param {Partial} theme - theme configuration. + * @returns {Partial} The generated Switch Ant theme configuration. + */ +export default ({ palette }: Partial): ComponentsTheme['Switch'] => ({ + handleBg: palette?.action?.secondary?.main, + handleShadow: 'transparent', +}) diff --git a/src/components/ThemeProvider/Ant.tsx b/src/components/ThemeProvider/Ant.tsx index bc6bd4c8..7a2d3390 100644 --- a/src/components/ThemeProvider/Ant.tsx +++ b/src/components/ThemeProvider/Ant.tsx @@ -25,6 +25,7 @@ import DividerTheme from '../Divider/Divider.theme' import FeedbackMessageTheme from '../FeedbackMessage/FeedbackMessage.theme' import InputTheme from '../BaseInput/BaseInput.theme' import MenuTheme from '../Menu/Menu.theme' +import SwitchTheme from '../Switch/Switch.theme' import TableTheme from '../Table/Table.theme' import Theme from '../../themes/schema' import { ThemeProviderProps } from './ThemeProvider.props' @@ -99,6 +100,7 @@ const generateAntTheme = ({ palette, typography, shape, spacing }: Partial Date: Wed, 30 Oct 2024 16:57:02 +0100 Subject: [PATCH 06/13] add: switch css customizations --- src/components/Switch/Switch.module.css | 57 +++++++++++++++++- src/components/Switch/Switch.props.ts | 10 ++-- src/components/Switch/Switch.test.tsx | 6 +- src/components/Switch/Switch.tsx | 6 +- .../Switch/__snapshots__/Switch.test.tsx.snap | 60 +++++++++---------- 5 files changed, 95 insertions(+), 44 deletions(-) diff --git a/src/components/Switch/Switch.module.css b/src/components/Switch/Switch.module.css index 6239882d..d65e8e5a 100644 --- a/src/components/Switch/Switch.module.css +++ b/src/components/Switch/Switch.module.css @@ -1,4 +1,4 @@ -.switchWrapper { +.switchComponent { display: flex; flex-direction: column; gap: var(--spacing-gap-xs, 4px); @@ -7,14 +7,69 @@ display: flex; align-items: center; gap: var(--spacing-gap-md, 12px); + color: var(--palette-text-neutral-main, #636363); } .switchDescription { padding-right: var(--spacing-padding-sm, 8px); padding-left: calc(var(--spacing-padding-sm, 8px) + var(--spacing-padding-3xl, 48px)); + color: var(--palette-text-neutral-main, #636363); &.small { padding-left: calc(var(--spacing-padding-sm, 8px) + var(--spacing-padding-2xl, 32px)); } } + + :global(.mia-platform-switch) { + background: var(--palette-action-secondary-bold, #cdcdcd); + + &:hover { + background: var(--palette-action-secondary-bolder, #acacac); + } + + /* Disabled switch */ + + &:global(.mia-platform-switch-disabled) { + opacity: 1; + background: var(--palette-action-disabled-main, #f2f2f2); + + &:hover { + background: var(--palette-action-disabled-main, #f2f2f2); + } + + :global(.mia-platform-switch-loading-icon) { + color: var(--palette-action-disabled-main, #f2f2f2); + } + } + + /* Checked switch */ + + &:global(.mia-platform-switch-checked) { + background: var(--palette-action-primary-default, #1261e4); + + &:hover { + background: var(--palette-action-primary-hover, #1890ff); + } + + /* Disabled and checked switch */ + + &:global(.mia-platform-switch-disabled) { + background: var(--palette-background-primary-0, #aad1fb); + + &:hover { + background: var(--palette-background-primary-0, #aad1fb); + } + + :global(.mia-platform-switch-loading-icon) { + color: var(--palette-background-primary-0, #aad1fb); + } + } + } + + /* Switch handle */ + + :global(.mia-platform-switch-handle::before) { + background: var(--palette-action-secondary-main, #fff); + } + } } diff --git a/src/components/Switch/Switch.props.ts b/src/components/Switch/Switch.props.ts index 5d621e05..51339e85 100644 --- a/src/components/Switch/Switch.props.ts +++ b/src/components/Switch/Switch.props.ts @@ -30,22 +30,22 @@ export type SwitchProps = { description?: ReactNode, /** - * Determines whether the switch is checked. + * Allows you to control whether the switch is checked. */ isChecked?: boolean, /** - * Determines whether the switch is disabled. Defaults to false. + * Allows you to control whether the switch is disabled. Defaults to false. */ isDisabled?: boolean, /** - * Determines whether the switch is in loading state. Defaults to false. + * Allows you to control whether the switch is checked on its first render. Defaults to false. */ isInitiallyChecked?: boolean, /** - * Determines whether the switch is in loading state. Defaults to false. + * Allows you to control whether the switch is in loading state. Defaults to false. */ isLoading?: boolean, @@ -53,14 +53,12 @@ export type SwitchProps = { * Function that is invoked when the switch state is changed. */ - // TODO: check this type (weird representation in storybook) onChange?: SwitchChangeEventHandler /** * Function that is invoked when the switch is clicked. */ - // TODO: check this type (weird representation in storybook) onClick?: SwitchClickEventHandler, /** diff --git a/src/components/Switch/Switch.test.tsx b/src/components/Switch/Switch.test.tsx index 1112de77..9ddd270a 100644 --- a/src/components/Switch/Switch.test.tsx +++ b/src/components/Switch/Switch.test.tsx @@ -22,13 +22,13 @@ describe('Switch', () => { expect(screen.getByRole('switch')).toBeChecked() }) - it('renders disabled state', () => { + it('renders default disabled state', () => { render() expect(screen.getByRole('switch')).toBeInTheDocument() expect(screen.getByRole('switch')).toBeDisabled() }) - it('renders disabled checked state', () => { + it('renders checked disabled state', () => { const { asFragment } = render() expect(screen.getByRole('switch')).toBeInTheDocument() expect(screen.getByRole('switch')).toBeDisabled() @@ -36,7 +36,7 @@ describe('Switch', () => { expect(asFragment()).toMatchSnapshot() }) - it('renders unchecked loading state', () => { + it('renders default loading state', () => { const { asFragment } = render() expect(screen.getByRole('switch')).toBeInTheDocument() expect(screen.getByRole('switch')).toBeDisabled() diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index 3ac1ee91..b061da89 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -27,7 +27,7 @@ import { SwitchProps } from './Switch.props' import styles from './Switch.module.css' const { - switchWrapper, + switchComponent, switchTextWrapper, switchDescription, small: smallSwitch, @@ -45,8 +45,6 @@ const antSizeRemapping = { [Size.Small]: 'small' as SwitchSize, } -// TODO: adapt tokens for different states (disabled, etc.) - export const Switch = ({ description, isChecked, @@ -64,7 +62,7 @@ export const Switch = ({ ]), [size]) return ( -
+