-
Notifications
You must be signed in to change notification settings - Fork 141
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* intial draft toggle * using bento css. added different stories and mixins. * removed user-select css
- Loading branch information
1 parent
afb844c
commit 878b6c1
Showing
7 changed files
with
344 additions
and
3 deletions.
There are no files selected for viewing
158 changes: 158 additions & 0 deletions
158
packages/lib/src/components/internal/Toggle/Toggle.scss
This file contains 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,158 @@ | ||
@use 'styles/mixins'; | ||
@import 'styles/variable-generator'; | ||
|
||
.adyen-checkout-toggle { | ||
$component-root: &; | ||
$label-padding: token(toggle-label-padding); | ||
|
||
color: inherit; | ||
cursor: pointer; | ||
display: flex; | ||
width: auto; | ||
|
||
@include mixins.box-sizing-setter(true); | ||
|
||
&--disabled { | ||
cursor: not-allowed; | ||
display: flex; | ||
} | ||
|
||
&--readonly { | ||
pointer-events: none; | ||
} | ||
|
||
&--label-first { | ||
align-items: flex-start; | ||
flex-direction: row-reverse; | ||
justify-content: flex-end; | ||
} | ||
|
||
&__input { | ||
cursor: inherit; | ||
opacity: 0; | ||
position: absolute; | ||
} | ||
|
||
&__track { | ||
align-items: center; | ||
background-color: token(toggle-track-background-color); | ||
border: token(toggle-track-border); | ||
border-radius: token(toggle-track-border-radius); | ||
display: flex; | ||
height: token(toggle-track-height); | ||
min-width: token(toggle-track-width); | ||
padding: token(toggle-track-padding); | ||
position: relative; | ||
|
||
#{$component-root}__input:focus-visible + & { | ||
@include mixins.b-focus-ring; | ||
} | ||
|
||
#{$component-root}__input:hover:enabled + & { | ||
background-color: token(toggle-track-hover-background-color); | ||
border-color: token(toggle-track-hover-border-color); | ||
} | ||
|
||
#{$component-root}__input:active:enabled + & { | ||
background-color: token(toggle-track-active-background-color); | ||
border-color: token(toggle-track-active-border-color); | ||
} | ||
|
||
#{$component-root}__input:disabled + & { | ||
background-color: token(toggle-track-disabled-background-color); | ||
border-color: token(toggle-track-disabled-border-color); | ||
cursor: not-allowed; | ||
|
||
path { | ||
fill: #8d95a3 | ||
} | ||
} | ||
|
||
#{$component-root}--readonly #{$component-root}__input + & { | ||
background-color: token(toggle-track-readonly-background-color); | ||
border-color: token(toggle-track-readonly-border-color); | ||
} | ||
|
||
#{$component-root}__input:checked + & { | ||
background-color: token(toggle-track-toggled-background-color); | ||
border: token(toggle-track-toggled-border); | ||
padding: token(toggle-track-toggled-padding); | ||
} | ||
|
||
#{$component-root}__input:checked:hover:enabled + & { | ||
background-color: token(toggle-track-toggled-hover-background-color); | ||
} | ||
|
||
#{$component-root}__input:checked:active:enabled + & { | ||
background-color: token(toggle-track-toggled-active-background-color); | ||
} | ||
|
||
#{$component-root}__input:checked:disabled + & { | ||
background-color: token(toggle-track-toggled-disabled-background-color); | ||
} | ||
|
||
#{$component-root}--readonly #{$component-root}__input:checked + & { | ||
background-color: token(toggle-track-toggled-readonly-background-color); | ||
} | ||
} | ||
|
||
&__handle { | ||
align-content: center; | ||
background-color: token(toggle-handle-background-color); | ||
border-radius: token(toggle-handle-border-radius); | ||
color: token(toggle-handle-toggled-color); | ||
display: inline-flex; | ||
height: token(toggle-handle-height); | ||
justify-content: center; | ||
transition: token(toggle-handle-transition); | ||
width: token(toggle-handle-width); | ||
|
||
#{$component-root}__input:disabled + * & { | ||
background-color: token(toggle-handle-disabled-background-color); | ||
cursor: not-allowed; | ||
} | ||
|
||
#{$component-root}__input:checked + * & { | ||
background-color: token(toggle-handle-toggled-background-color); | ||
height: token(toggle-handle-toggled-height); | ||
transform: translateX(100%); | ||
width: token(toggle-handle-toggled-width); | ||
} | ||
|
||
#{$component-root}__input:checked:disabled + * & { | ||
background-color: token(toggle-handle-toggled-disabled-background-color); | ||
color: token(toggle-handle-toggled-disabled-color); | ||
cursor: not-allowed; | ||
} | ||
|
||
#{$component-root}--readonly #{$component-root}__input:checked + * & { | ||
background-color: token(toggle-handle-toggled-readonly-background-color); | ||
} | ||
} | ||
|
||
&__label-container { | ||
display: flex; | ||
flex-direction: column; | ||
padding-left: $label-padding; | ||
|
||
@include mixins.adyen-checkout-text-body; | ||
|
||
#{$component-root}--label-first > & { | ||
padding-left: 0; | ||
padding-right: $label-padding; | ||
} | ||
} | ||
|
||
&__label { | ||
vertical-align: baseline; | ||
|
||
@include mixins.adyen-checkout-text-body; | ||
} | ||
|
||
&__description { | ||
color: token(toggle-description-color); | ||
padding-top: token(toggle-description-padding); | ||
|
||
@include mixins.adyen-checkout-text-body; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
packages/lib/src/components/internal/Toggle/Toggle.test.tsx
This file contains 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,32 @@ | ||
import { h } from 'preact'; | ||
import { render, screen } from '@testing-library/preact'; | ||
import userEvent from '@testing-library/user-event'; | ||
import Toggle from './Toggle'; | ||
|
||
test('should emit correct values', async () => { | ||
const user = userEvent.setup(); | ||
const onChangeMock = jest.fn(); | ||
|
||
render(<Toggle checked={false} onChange={onChangeMock} />); | ||
|
||
await user.click(screen.getByRole('switch')); | ||
expect(onChangeMock.mock.calls[0][0]).toBe(true); | ||
|
||
await user.click(screen.getByRole('switch')); | ||
expect(onChangeMock.mock.calls[1][0]).toBe(false); | ||
}); | ||
|
||
test('should render as readonly', () => { | ||
render(<Toggle checked={false} readonly />); | ||
expect(screen.getByRole('switch').getAttribute('aria-readonly')).toBe('true'); | ||
}); | ||
|
||
test('should render as disabled', () => { | ||
render(<Toggle checked={false} disabled />); | ||
expect(screen.getByRole('switch')).toBeDisabled(); | ||
}); | ||
|
||
test('should render description', () => { | ||
render(<Toggle checked={false} label="Save details" description="Save all details" />); | ||
expect(screen.getByText('Save all details')).toBeTruthy(); | ||
}); |
This file contains 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,78 @@ | ||
import { h } from 'preact'; | ||
import { useCallback, useMemo } from 'preact/hooks'; | ||
import cx from 'classnames'; | ||
import uuid from '../../../utils/uuid'; | ||
import './Toggle.scss'; | ||
|
||
interface ToggleProps { | ||
label?: string; | ||
labelPosition?: 'before' | 'after'; | ||
ariaLabel?: string; | ||
description?: string; | ||
checked: boolean; | ||
disabled?: boolean; | ||
readonly?: boolean; | ||
onChange?(checked: boolean): void; | ||
} | ||
|
||
const Toggle = ({ label, labelPosition = 'after', ariaLabel, description, checked, disabled = false, readonly = false, onChange }: ToggleProps) => { | ||
const descriptionId = useMemo(() => (description ? `toggle-description-${uuid()}` : null), [description]); | ||
const computedAriaLabel = useMemo(() => ariaLabel ?? label, [ariaLabel, label]); | ||
|
||
const conditionalClasses = cx({ | ||
'adyen-checkout-toggle--label-first': labelPosition === 'before', | ||
'adyen-checkout-toggle--disabled': disabled, | ||
'adyen-checkout-toggle--readonly': readonly | ||
}); | ||
|
||
const onInputChange = useCallback( | ||
(event: Event) => { | ||
onChange((event.target as HTMLInputElement).checked); | ||
}, | ||
[onChange] | ||
); | ||
|
||
return ( | ||
<label className={`adyen-checkout-toggle ${conditionalClasses}`}> | ||
<input | ||
disabled={disabled} | ||
checked={checked} | ||
onChange={onInputChange} | ||
aria-label={computedAriaLabel} | ||
aria-readonly={readonly} | ||
aria-describedby={descriptionId} | ||
role="switch" | ||
type="checkbox" | ||
className="adyen-checkout-toggle__input" | ||
/> | ||
|
||
<span aria-hidden={true} className="adyen-checkout-toggle__track"> | ||
<span className="adyen-checkout-toggle__handle"> | ||
{checked && ( | ||
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"> | ||
<path | ||
fill="#00112C" | ||
d="M12.0608 6.00011L11.0001 4.93945L7.00011 8.93945L5.00011 6.93945L3.93945 8.00011L7.00011 11.0608L12.0608 6.00011Z" | ||
></path> | ||
</svg> | ||
)} | ||
</span> | ||
</span> | ||
|
||
{label && ( | ||
<span className="adyen-checkout-toggle__label-container"> | ||
<span className="adyen-checkout-toggle__label-text" data-testid="inner-label"> | ||
{label} | ||
</span> | ||
{description && ( | ||
<span data-testid="description" className="adyen-checkout-toggle__description" id={descriptionId}> | ||
{description} | ||
</span> | ||
)} | ||
</span> | ||
)} | ||
</label> | ||
); | ||
}; | ||
|
||
export default Toggle; |
This file contains 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 @@ | ||
export { default } from './Toggle'; |
This file contains 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 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
65 changes: 65 additions & 0 deletions
65
packages/lib/storybook/stories/internals/Toggle.stories.tsx
This file contains 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,65 @@ | ||
import { Meta, StoryObj } from '@storybook/preact'; | ||
import Toggle from '../../../src/components/internal/Toggle'; | ||
import { useState } from 'preact/hooks'; | ||
|
||
const meta: Meta = { | ||
title: 'Internals/Toggle', | ||
component: Toggle | ||
}; | ||
|
||
export const Default: StoryObj = { | ||
render: () => { | ||
const [checked, setChecked] = useState<boolean>(false); | ||
return <Toggle checked={checked} onChange={setChecked} label={'Save your profile'} />; | ||
}, | ||
parameters: { | ||
controls: { exclude: ['useSessions', 'countryCode', 'shopperLocale', 'amount', 'showPayButton'] } | ||
} | ||
}; | ||
|
||
export const Disabled: StoryObj = { | ||
render: () => { | ||
return <Toggle checked={false} disabled label={'Save your profile'} />; | ||
}, | ||
parameters: { | ||
controls: { exclude: ['useSessions', 'countryCode', 'shopperLocale', 'amount', 'showPayButton'] } | ||
} | ||
}; | ||
|
||
export const Readonly: StoryObj = { | ||
render: () => { | ||
return <Toggle checked={false} readonly label={'Save your profile'} />; | ||
}, | ||
parameters: { | ||
controls: { exclude: ['useSessions', 'countryCode', 'shopperLocale', 'amount', 'showPayButton'] } | ||
} | ||
}; | ||
|
||
export const ToggleOnly: StoryObj = { | ||
render: () => { | ||
const [checked, setChecked] = useState<boolean>(true); | ||
return <Toggle checked={checked} onChange={setChecked} />; | ||
}, | ||
parameters: { | ||
controls: { exclude: ['useSessions', 'countryCode', 'shopperLocale', 'amount', 'showPayButton'] } | ||
} | ||
}; | ||
|
||
export const WithDescription: StoryObj = { | ||
render: () => { | ||
const [checked, setChecked] = useState<boolean>(true); | ||
return ( | ||
<Toggle | ||
checked={checked} | ||
onChange={setChecked} | ||
label={'Save your profile'} | ||
description={'Save your profile for faster checkouts next time you buy our products'} | ||
/> | ||
); | ||
}, | ||
parameters: { | ||
controls: { exclude: ['useSessions', 'countryCode', 'shopperLocale', 'amount', 'showPayButton'] } | ||
} | ||
}; | ||
|
||
export default meta; |