-
Notifications
You must be signed in to change notification settings - Fork 7
feat: switch #736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
feat: switch #736
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
6bbe654
feat: add Switch component with customizable shapes and variants
AbhishekA1509 0228618
feat: enhance Switch component with size and loading state support
AbhishekA1509 cb4315d
feat: enhance Switch component with accessibility improvements and ne…
AbhishekA1509 daa9daa
feat: update Switch component styles and improve index exports
AbhishekA1509 652b7e1
feat: make ariaLabel required for SwitchProps to enhance accessibility
AbhishekA1509 478600e
Merge branch 'develop' of https://github.com/devtron-labs/devtron-fe-…
AbhishekA1509 4297876
feat: enhance Switch component with loading state handling and improv…
AbhishekA1509 5ff39fc
chore: update version from 1.13.0-pre-6 to 1.13.0-beta-8 in package.j…
AbhishekA1509 95faa76
refactor: rename SwitchProps to DTSwitchProps for consistency across …
AbhishekA1509 b263a1d
feat: add hover color support for Switch component and refactor styles
AbhishekA1509 b8f14c0
fix: remove unnecessary keys from loading and icon spans in Switch co…
AbhishekA1509 306bd3c
refactor: remove unused SWITCH_VARIANTS and SWITCH_SHAPES constants f…
AbhishekA1509 4f03159
Merge branch 'develop' of https://github.com/devtron-labs/devtron-fe-…
AbhishekA1509 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| import { AriaAttributes, useRef } from 'react' | ||
| import { AnimatePresence, motion } from 'framer-motion' | ||
|
|
||
| import { Tooltip } from '@Common/Tooltip' | ||
| import { ComponentSizeType } from '@Shared/constants' | ||
| import { getUniqueId } from '@Shared/Helpers' | ||
|
|
||
| import { Icon } from '../Icon' | ||
| import { INDETERMINATE_ICON_WIDTH_MAP, LOADING_COLOR_MAP } from './constants' | ||
| import { DTSwitchProps } from './types' | ||
| import { | ||
| getSwitchContainerClass, | ||
| getSwitchIconColor, | ||
| getSwitchThumbClass, | ||
| getSwitchTrackColor, | ||
| getSwitchTrackHoverColor, | ||
| getThumbPadding, | ||
| getThumbPosition, | ||
| } from './utils' | ||
|
|
||
| import './switch.scss' | ||
|
|
||
| const Switch = ({ | ||
| ariaLabel, | ||
| dataTestId, | ||
| isDisabled, | ||
| isLoading, | ||
| isChecked, | ||
| tooltipContent, | ||
| shape = 'rounded', | ||
| variant = 'positive', | ||
| iconColor, | ||
| iconName, | ||
| indeterminate = false, | ||
| size = ComponentSizeType.medium, | ||
| name, | ||
| onChange, | ||
| }: DTSwitchProps) => { | ||
| const inputId = useRef(getUniqueId()) | ||
|
|
||
| const getAriaCheckedValue = (): AriaAttributes['aria-checked'] => { | ||
| if (!isChecked) { | ||
| return false | ||
| } | ||
|
|
||
| return indeterminate ? 'mixed' : true | ||
| } | ||
|
|
||
| const ariaCheckedValue = getAriaCheckedValue() | ||
|
|
||
| const showIndeterminateIcon = ariaCheckedValue === 'mixed' | ||
|
|
||
| const renderContent = () => ( | ||
| <motion.span | ||
| className={`flex flex-grow-1 ${getThumbPadding({ shape, isLoading })} ${getThumbPosition({ isChecked, isLoading })}`} | ||
| layout | ||
| transition={{ ease: 'easeInOut', duration: 0.2 }} | ||
| > | ||
| {isLoading ? ( | ||
| <motion.span | ||
| transition={{ ease: 'easeInOut', duration: 0.2 }} | ||
| layoutId={`${name}-loader`} | ||
| className="flex-grow-1 h-100 dc__fill-available-space dc__no-shrink" | ||
| > | ||
| <Icon name="ic-circle-loader" color={LOADING_COLOR_MAP[variant]} size={null} /> | ||
| </motion.span> | ||
| ) : ( | ||
| <motion.span | ||
| layoutId={`${name}-thumb`} | ||
| className={getSwitchThumbClass({ shape, size, showIndeterminateIcon })} | ||
| layout | ||
| transition={{ ease: 'easeInOut', duration: 0.2 }} | ||
| > | ||
| <AnimatePresence> | ||
| {showIndeterminateIcon ? ( | ||
| <motion.span | ||
| className={`${INDETERMINATE_ICON_WIDTH_MAP[size]} h-2 br-4 dc__no-shrink bg__white`} | ||
| initial={{ scale: 0, opacity: 0 }} | ||
| animate={{ scale: 1, opacity: 1 }} | ||
| exit={{ scale: 0, opacity: 0 }} | ||
| /> | ||
| ) : ( | ||
| iconName && ( | ||
| <motion.span | ||
| className="icon-dim-12 flex dc__fill-available-space dc__no-shrink" | ||
| initial={{ scale: 0.8, opacity: 0 }} | ||
| animate={{ scale: 1, opacity: 1 }} | ||
| exit={{ scale: 0.8, opacity: 0 }} | ||
| > | ||
| <Icon | ||
| name={iconName} | ||
| color={getSwitchIconColor({ | ||
| isChecked, | ||
| iconColor, | ||
| variant, | ||
| })} | ||
| size={null} | ||
| /> | ||
| </motion.span> | ||
| ) | ||
| )} | ||
| </AnimatePresence> | ||
| </motion.span> | ||
| )} | ||
| </motion.span> | ||
| ) | ||
|
|
||
| return ( | ||
| <Tooltip alwaysShowTippyOnHover={!!tooltipContent} content={tooltipContent}> | ||
| <label | ||
| htmlFor={inputId.current} | ||
| className={`${getSwitchContainerClass({ shape, size })} flex dc__no-shrink py-2 m-0`} | ||
| > | ||
| <input | ||
| type="checkbox" | ||
| id={inputId.current} | ||
| name={name} | ||
| checked={isChecked} | ||
| disabled={isDisabled} | ||
| readOnly | ||
| hidden | ||
| /> | ||
|
|
||
| <button | ||
| type="button" | ||
| role="checkbox" | ||
| aria-checked={ariaCheckedValue} | ||
| aria-labelledby={inputId.current} | ||
| aria-label={isLoading ? 'Loading...' : ariaLabel} | ||
| data-testid={dataTestId} | ||
| disabled={isDisabled || isLoading} | ||
| aria-disabled={isDisabled} | ||
| className={`p-0-imp h-100 flex flex-grow-1 dc__no-border dt-switch__track ${shape === 'rounded' ? 'br-12' : 'br-4'} ${getSwitchTrackColor({ shape, variant, isChecked, isLoading })} ${isDisabled ? 'dc__disabled' : ''} dc__fill-available-space`} | ||
| onClick={onChange} | ||
| style={{ | ||
| // Adding hover styles directly to the button | ||
| ['--switch-track-hover-color' as string]: getSwitchTrackHoverColor({ | ||
| shape, | ||
| variant, | ||
| isChecked, | ||
| }), | ||
| }} | ||
| > | ||
| {renderContent()} | ||
| </button> | ||
| </label> | ||
| </Tooltip> | ||
| ) | ||
| } | ||
|
|
||
| export default Switch |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { ComponentSizeType } from '@Shared/constants' | ||
| import { IconBaseColorType } from '@Shared/types' | ||
|
|
||
| import { DTSwitchProps } from './types' | ||
|
|
||
| export const ROUNDED_SWITCH_SIZE_MAP: Readonly<Record<DTSwitchProps['size'], string>> = { | ||
| [ComponentSizeType.medium]: 'w-32', | ||
| [ComponentSizeType.small]: 'w-24', | ||
| } | ||
|
|
||
| export const SQUARE_SWITCH_SIZE_MAP: typeof ROUNDED_SWITCH_SIZE_MAP = { | ||
| [ComponentSizeType.medium]: 'w-28', | ||
| [ComponentSizeType.small]: 'w-24', | ||
| } | ||
|
|
||
| export const SWITCH_HEIGHT_MAP: Readonly<Record<DTSwitchProps['size'], string>> = { | ||
| [ComponentSizeType.medium]: 'h-24', | ||
| [ComponentSizeType.small]: 'h-20', | ||
| } | ||
|
|
||
| export const LOADING_COLOR_MAP: Record<DTSwitchProps['variant'], IconBaseColorType> = { | ||
| theme: 'B500', | ||
| positive: 'G500', | ||
| } | ||
|
|
||
| export const ROUNDED_SWITCH_TRACK_COLOR_MAP: Record<DTSwitchProps['variant'], string> = { | ||
| theme: 'bcb-5', | ||
| positive: 'bcg-5', | ||
| } | ||
|
|
||
| export const ROUNDED_SWITCH_TRACK_HOVER_COLOR_MAP: Record<DTSwitchProps['variant'], `var(--${IconBaseColorType})`> = { | ||
| theme: 'var(--B600)', | ||
| positive: 'var(--G600)', | ||
| } | ||
|
|
||
| export const SQUARE_SWITCH_TRACK_COLOR_MAP: typeof ROUNDED_SWITCH_TRACK_COLOR_MAP = { | ||
| theme: 'bcb-3', | ||
| positive: 'bcg-3', | ||
| } | ||
|
|
||
| export const SQUARE_SWITCH_TRACK_HOVER_COLOR_MAP: typeof ROUNDED_SWITCH_TRACK_HOVER_COLOR_MAP = { | ||
| theme: 'var(--B400)', | ||
| positive: 'var(--G400)', | ||
| } | ||
|
|
||
| export const ROUNDED_SWITCH_THUMB_SIZE_MAP: Record<DTSwitchProps['size'], string> = { | ||
| [ComponentSizeType.medium]: 'icon-dim-16', | ||
| [ComponentSizeType.small]: 'icon-dim-12', | ||
| } | ||
|
|
||
| export const INDETERMINATE_ICON_WIDTH_MAP: Record<DTSwitchProps['size'], string> = { | ||
| [ComponentSizeType.medium]: 'w-12', | ||
| [ComponentSizeType.small]: 'w-10', | ||
| } | ||
|
|
||
| export const SWITCH_THUMB_PADDING_MAP: Record<DTSwitchProps['size'], string> = { | ||
| [ComponentSizeType.medium]: 'p-3', | ||
| [ComponentSizeType.small]: 'p-1', | ||
| } | ||
|
|
||
| export const THUMB_OUTER_PADDING_MAP: Record<DTSwitchProps['shape'], string> = { | ||
| rounded: 'p-2', | ||
| square: 'p-1', | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export { default as DTSwitch } from './Switch.component' | ||
| export type { DTSwitchProps } from './types' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| .dt-switch { | ||
| &__track { | ||
| --switch-track-hover-color: 'transparent'; | ||
|
|
||
| &:hover { | ||
| background-color: var(--switch-track-hover-color); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| import { ComponentSizeType } from '@Shared/constants' | ||
| import { IconBaseColorType } from '@Shared/types' | ||
|
|
||
| import { IconName } from '../Icon' | ||
|
|
||
| /** | ||
| * Represents the properties for configuring the shape and behavior of a switch component. | ||
| * | ||
| * - When `shape` is `rounded`: | ||
| * - The switch will have a rounded appearance. | ||
| * - `iconName`, `iconColor`, and `indeterminate` are not applicable. | ||
| * | ||
| * - When `shape` is `square`: | ||
| * - The switch will have a square appearance. | ||
| * - `iconName` specifies the name of the icon to display. | ||
| * - `iconColor` allows customization of the icon's color in the active state. | ||
| * - `indeterminate` indicates whether the switch is in an indeterminate state, typically used for checkboxes to represent a mixed state. | ||
| * If `indeterminate` is true, the switch will not be fully checked or unchecked. | ||
| */ | ||
| type SwitchShapeProps = | ||
| | { | ||
| /** | ||
| * The shape of the switch. Defaults to `rounded` if not specified. | ||
| */ | ||
| shape?: 'rounded' | ||
|
|
||
| /** | ||
| * Icon name is not applicable for the `rounded` shape. | ||
| */ | ||
| iconName?: never | ||
|
|
||
| /** | ||
| * Icon color is not applicable for the `rounded` shape. | ||
| */ | ||
| iconColor?: never | ||
| /** | ||
| * Indicates whether the switch is in an indeterminate state. | ||
| * This state is typically used for checkboxes to indicate a mixed state. | ||
| * If true, the switch will not be fully checked or unchecked. Due this state alone we are keeping role as `checkbox` instead of `switch`. | ||
| * This property is not applicable for the `square` shape. | ||
| * @default false | ||
| */ | ||
| indeterminate?: boolean | ||
| } | ||
| | { | ||
| /** | ||
| * The shape of the switch. Must be `square` to enable icon-related properties. | ||
| */ | ||
| shape: 'square' | ||
|
|
||
| /** | ||
| * The name of the icon to display when the shape is `square`. | ||
| */ | ||
| iconName: IconName | ||
|
|
||
| /** | ||
| * The color of the icon. If provided, this will override the default color in the active state. | ||
| */ | ||
| iconColor?: IconBaseColorType | ||
| indeterminate?: never | ||
| } | ||
|
|
||
| /** | ||
| * Represents the properties for the `Switch` component. | ||
| */ | ||
| export type DTSwitchProps = { | ||
| /** | ||
| * The ARIA label for the switch, used for accessibility purposes. | ||
| */ | ||
| ariaLabel: string | ||
|
|
||
| /** | ||
| * Used in forms to identify the switch. | ||
| */ | ||
| name: string | ||
|
|
||
| /** | ||
| * A unique identifier for testing purposes. | ||
| */ | ||
| dataTestId: string | ||
|
|
||
| /** | ||
| * The visual variant of the switch. | ||
| * | ||
| * @default `positive` | ||
| */ | ||
| variant?: 'theme' | 'positive' | ||
|
|
||
| /** | ||
| * The size of the switch. | ||
| * @default `ComponentSizeType.medium` | ||
| */ | ||
| size?: Extract<ComponentSizeType, ComponentSizeType.medium | ComponentSizeType.small> | ||
|
|
||
| /** | ||
| * Callback function that is called when the switch state changes. | ||
| * This function should handle the logic for toggling the switch. | ||
| */ | ||
| onChange: () => void | ||
|
|
||
| /** | ||
| * Indicates whether the switch is disabled. | ||
| */ | ||
| isDisabled?: boolean | ||
|
|
||
| /** | ||
| * Indicates whether the switch is in a loading state. | ||
| */ | ||
| isLoading?: boolean | ||
|
|
||
| /** | ||
| * Indicates whether the switch is currently checked (on). | ||
| */ | ||
| isChecked: boolean | ||
|
|
||
| /** | ||
| * Optional tooltip content to display when hovering over the switch. | ||
| * | ||
| * @default undefined | ||
| */ | ||
| tooltipContent?: string | ||
| } & SwitchShapeProps | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.