Skip to content

Commit

Permalink
#344 Handle Switch disabled state visually (#353)
Browse files Browse the repository at this point in the history
* Lock icon added for the disabled state,
* Applied proper color tokens for `hover`, `enabled`, and `disabled` cases,
* Changed naming convention as `enabled/disabled` was easy to be confused with `checked (on)/unchecked (off)`,
* Added multiple stories representing the changes,
* Used `--color-action-default-rgb` for `box-shadow`
* Added `StoryDescriptor` component for wrapping components that require additional description
  • Loading branch information
Szymon Graczyk authored Jul 26, 2022
1 parent 65e3c89 commit 6a0c3ca
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 88 deletions.
16 changes: 13 additions & 3 deletions packages/react-components/.storybook/preview-head.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,20 @@
color: var(--content-default);
}

.spacer {
.story-container {
margin-bottom: 10px;
}

.story-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 5px;
}

.story-spacer {
margin-bottom: 5px;
}
.spacer > * + * {
.story-spacer > * + * {
margin-left: 5px;
}

Expand All @@ -24,7 +34,7 @@

* {
box-sizing: border-box;
font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, Segoe UI,
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, Segoe UI,
Helvetica Neue, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ button.args = {

export const kinds = (): React.ReactElement => (
<div>
<div className="spacer">
<div className="story-spacer">
<Button>Basic</Button>
<Button kind="primary">Primary</Button>
<Button kind="secondary">Secondary</Button>
Expand All @@ -65,7 +65,7 @@ export const kinds = (): React.ReactElement => (

export const states = (): React.ReactElement => (
<div>
<div className="spacer">
<div className="story-spacer">
<Button disabled>Disabled</Button>
<Button disabled kind="primary">
Disabled
Expand All @@ -86,7 +86,7 @@ export const states = (): React.ReactElement => (
Disabled
</Button>
</div>
<div className="spacer">
<div className="story-spacer">
<Button loading>Loading</Button>
<Button loading kind="primary">
Loading
Expand All @@ -112,7 +112,7 @@ export const states = (): React.ReactElement => (

export const sizes = (): React.ReactElement => (
<>
<div className="spacer">
<div className="story-spacer">
<Button size="compact" kind="primary">
Compact
</Button>
Expand All @@ -123,7 +123,7 @@ export const sizes = (): React.ReactElement => (
Large
</Button>
</div>
<div className="spacer">
<div className="story-spacer">
<Button
icon={<Icon source={MaterialIcons.AddCircle} />}
size="compact"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const FileUploadProgressStates: React.FC = () => {

return (
<div>
<div className="spacer" style={{ marginBottom: 30 }}>
<div className="story-spacer" style={{ marginBottom: 30 }}>
<Button onClick={() => setStatus('normal')}>Default</Button>
<Button kind="primary" onClick={() => setStatus('success')}>
Success
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default {
} as ComponentMeta<typeof Loader>;

export const sizes = (): React.ReactElement => (
<div className="spacer">
<div className="story-spacer">
<Loader size="small" />
<Loader size="medium" />
<Loader size="large"></Loader>
Expand Down
48 changes: 21 additions & 27 deletions packages/react-components/src/components/Search/Search.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import * as React from 'react';
import { ComponentMeta, Story } from '@storybook/react';

import { StoryDescriptor } from '../../stories/components/StoryDescriptor';

import {
SearchInput as SearchComponent,
ISearchInputProps,
SearchSize,
} from './Search';

const commonWidth: React.CSSProperties = { width: 300 };

export default {
title: 'Components/Search',
component: SearchComponent,
argTypes: { onChange: { action: 'changed' } },
} as ComponentMeta<typeof SearchComponent>;

const containerStyles = { width: 300, marginBottom: 15 };
const textStyles = { marginBottom: 5, fontSize: 15 };

const StoryTemplate: Story<ISearchInputProps> = (args: ISearchInputProps) => (
<div style={{ width: 300 }}>
<div style={commonWidth}>
<SearchComponent {...args} />
</div>
);
Expand All @@ -26,42 +27,35 @@ export const Search = StoryTemplate.bind({});
Search.args = {};

export const States = (args: ISearchInputProps): React.ReactElement => (
<div>
<div style={containerStyles}>
<div style={textStyles}>Basic</div>
<div style={commonWidth}>
<StoryDescriptor title="Basic">
<SearchComponent {...args} />
</div>
<div style={containerStyles}>
<div style={textStyles}>Disabled</div>
</StoryDescriptor>
<StoryDescriptor title="Disabled">
<SearchComponent {...args} isDisabled />
</div>
<div style={containerStyles}>
<div style={textStyles}>Loading</div>
</StoryDescriptor>
<StoryDescriptor title="Loading">
<SearchComponent {...args} isLoading />
</div>
<div style={containerStyles}>
<div style={textStyles}>Collapsable</div>
</StoryDescriptor>
<StoryDescriptor title="Collapsible">
<SearchComponent {...args} isCollapsable />
</div>
</StoryDescriptor>
</div>
);

States.args = {};

export const Sizes = (args: ISearchInputProps): React.ReactElement => (
<div>
<div style={containerStyles}>
<div style={textStyles}>Compact</div>
<div style={commonWidth}>
<StoryDescriptor title="Compact">
<SearchComponent {...args} size={SearchSize.Compact} />
</div>
<div style={containerStyles}>
<div style={textStyles}>Medium</div>
</StoryDescriptor>
<StoryDescriptor title="Medium">
<SearchComponent {...args} size={SearchSize.Medium} />
</div>
<div style={containerStyles}>
<div style={textStyles}>Large</div>
</StoryDescriptor>
<StoryDescriptor title="Large">
<SearchComponent {...args} size={SearchSize.Large} />
</div>
</StoryDescriptor>
</div>
);

Expand Down
44 changes: 29 additions & 15 deletions packages/react-components/src/components/Switch/Switch.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ $compact-move: $compact-width - $compact-size;
position: relative;

&:hover {
.#{$base-class}__track {
&--enabled {
background-color: var(--color-positive-default);
}
$hovered-track: #{$base-class}__track;

&--disabled {
background-color: var(--surface-secondary-default);
}
.#{$hovered-track}--enabled.#{$hovered-track}--on {
background-color: var(--color-positive-hover);
}

.#{$hovered-track}--enabled.#{$hovered-track}--off {
background-color: var(--surface-secondary-hover);
}
}

Expand All @@ -46,12 +46,15 @@ $compact-move: $compact-width - $compact-size;
margin: 0;
opacity: 0;
width: 100%;

&--disabled {
cursor: not-allowed;
}
}

&__input:focus + .#{$base-class}__container {
.#{$base-class}__track {
//TODO use shadow token instead of hex color
box-shadow: 0 0 1px 2px rgba(#4379d6, 0.7);
box-shadow: 0 0 1px 2px rgba(var(--color-action-default-rgb), 0.7);
}
}

Expand All @@ -74,18 +77,29 @@ $compact-move: $compact-width - $compact-size;
transition-property: background-color;
transition-timing-function: $switch-transition-timing-function;

&--enabled {
&--on#{&}--enabled {
background-color: var(--color-positive-default);
}

&--disabled {
&--on#{&}--disabled {
background-color: var(--color-positive-disabled);
}

&--off#{&}--enabled {
background-color: var(--surface-secondary-default);
}

&--off#{&}--disabled {
background-color: var(--surface-secondary-disabled);
}
}

&__slider {
align-items: center;
background: var(--content-white-locked);
border-radius: 50%;
display: flex;
justify-content: center;
position: absolute;
top: 50%;
transform: translateY(-50%);
Expand All @@ -97,12 +111,12 @@ $compact-move: $compact-width - $compact-size;
height: calc(#{$basic-size});
width: calc(#{$basic-size});

&--enabled {
&--on {
left: calc(#{$basic-move} - 2px);
right: 2px;
}

&--disabled {
&--off {
left: 2px;
right: calc(#{$basic-move} - 2px);
}
Expand All @@ -112,12 +126,12 @@ $compact-move: $compact-width - $compact-size;
height: calc(#{$compact-size});
width: calc(#{$compact-size});

&--enabled {
&--on {
left: calc(#{$compact-move} - 2px);
right: 2px;
}

&--disabled {
&--off {
left: 2px;
right: calc(#{$compact-move} - 2px);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,27 @@ describe('Switch', () => {
expect(checkbox.checked).toEqual(false);
});

it('should change state to enabled when user clicks on disabled switch', () => {
it('should change state to checked (on) when user clicks on unchecked (off) switch', () => {
const { getByRole } = render(<Switch />);
const checkbox = getByRole('checkbox') as HTMLInputElement;
expect(checkbox.checked).toEqual(false);
fireEvent.click(checkbox);
expect(checkbox.checked).toEqual(true);
});

it('should change state to disabled when user clicks on enabled switch', () => {
it('should change state to unchecked (off) when user clicks on checked (on) switch', () => {
const { getByRole } = render(<Switch on={true} />);
const checkbox = getByRole('checkbox') as HTMLInputElement;
expect(checkbox.checked).toEqual(true);
fireEvent.click(checkbox);
expect(checkbox.checked).toEqual(false);
});

it('should be enabled if defaultOn is set to true', () => {
it('should be checked (on) if defaultOn is set to true', () => {
const { getByRole } = render(<Switch defaultOn={true} />);
const checkbox = getByRole('checkbox') as HTMLInputElement;
expect(checkbox.checked).toEqual(true);
fireEvent.click(checkbox);
expect(checkbox.checked).toEqual(false);
});
});
1;
49 changes: 33 additions & 16 deletions packages/react-components/src/components/Switch/Switch.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
import * as React from 'react';
import { ComponentMeta } from '@storybook/react';
import { ComponentMeta, Story } from '@storybook/react';

import { Switch as SwitchComponent, SwitchProps } from './Switch';
import noop from '../../utils/noop';
import { StoryDescriptor } from '../../stories/components/StoryDescriptor';

import { Switch, SwitchProps } from './Switch';

export default {
title: 'Components/Switch',
component: SwitchComponent,
} as ComponentMeta<typeof SwitchComponent>;
component: Switch,
} as ComponentMeta<typeof Switch>;

export const Default: Story<SwitchProps> = (args: SwitchProps) => (
<Switch {...args} onChange={undefined} />
);
Default.storyName = 'Switch';

export const Switch = (args: SwitchProps): React.ReactElement => {
return (
<div>
<SwitchComponent {...args} />
</div>
);
};
export const States = (): JSX.Element => (
<>
<StoryDescriptor title="Enabled">
<Switch on={true} />
<Switch on={false} />
</StoryDescriptor>
<StoryDescriptor title="Disabled">
<Switch on={true} disabled={true} />
<Switch on={false} disabled={true} />
</StoryDescriptor>
</>
);

Switch.args = {
size: 'basic',
onChange: noop,
};
export const Sizes = (): JSX.Element => (
<>
<StoryDescriptor title="Compact">
<Switch size="compact" />
</StoryDescriptor>
<StoryDescriptor title="Basic">
<Switch size="basic" />
</StoryDescriptor>
</>
);
Loading

0 comments on commit 6a0c3ca

Please sign in to comment.