Skip to content
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

🪟 🎨 Aligns Switch design according to the latest Figma updates #20629

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ exports[`CreateConnectionForm should render 1`] = `
class="<removed-for-snapshot-test>"
>
<input
aria-checked="true"
checked=""
class="<removed-for-snapshot-test>"
data-testid="pokemon-stream-sync-switch"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Switch } from "components/ui/Switch";

import styles from "./LabeledSwitch.module.scss";

interface LabeledSwitchProps extends React.InputHTMLAttributes<HTMLInputElement> {
interface LabeledSwitchProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
message?: React.ReactNode;
label?: React.ReactNode;
checkbox?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const BulkHeader: React.FC = () => {
<CheckboxCell />
<ArrowCell />
<HeaderCell flex={0.4}>
<Switch small checked={options.selected} onChange={() => onChangeOption({ selected: !options.selected })} />
<Switch size="sm" checked={options.selected} onChange={() => onChangeOption({ selected: !options.selected })} />
</HeaderCell>
<HeaderCell />
<HeaderCell />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ const FieldRowInner: React.FC<FieldRowProps> = ({
<Cell flex={0}>
<SyncCheckboxContainer>
{!isNestedField && (
<Switch small checked={isSelected} onChange={() => onToggleFieldSelected(field.path, !isSelected)} />
<Switch size="sm" checked={isSelected} onChange={() => onToggleFieldSelected(field.path, !isSelected)} />
)}
{isNestedField && (
<Tooltip control={<Switch small disabled checked={isSelected} />}>
<Tooltip control={<Switch size="sm" disabled checked={isSelected} />}>
<FormattedMessage id="form.field.sync.nestedFieldTooltip" values={{ fieldName: field.path[0] }} />
</Tooltip>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const StreamHeader: React.FC<StreamHeaderProps> = ({
<div className={streamHeaderContentStyle}>
<HeaderCell flex={0.4}>
<Switch
small
size="sm"
checked={stream.config?.selected}
onChange={onSelectStream}
disabled={disabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,12 @@ export const BulkEditPanel: React.FC = () => {
<FormattedMessage id="sources.sync" />
</p>
<div className={styles.syncCellContent}>
<Switch small checked={options.selected} onChange={() => onChangeOption({ selected: !options.selected })} />
<Switch
variant="strong-blue"
size="sm"
checked={options.selected}
onChange={() => onChangeOption({ selected: !options.selected })}
/>
</div>
</HeaderCell>
<HeaderCell flex={1} className={styles.headerCell}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const CatalogTreeTableRow: React.FC<StreamHeaderProps> = ({
</div>
)}
<Cell flex={0.5} flush>
<Switch small checked={stream.config?.selected} onChange={onSelectStream} disabled={disabled} />
<Switch size="sm" checked={stream.config?.selected} onChange={onSelectStream} disabled={disabled} />
</Cell>
{/* <Cell>{fieldCount}</Cell> */}
<Cell flex={1} title={stream.stream?.namespace || ""}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const StreamPanelHeader: React.FC<StreamPanelHeaderProps> = ({
return (
<div className={styles.container}>
<div>
<Switch small checked={config?.selected} onChange={onSelectedChange} disabled={disabled} />
<Switch size="sm" checked={config?.selected} onChange={onSelectedChange} disabled={disabled} />
</div>
<div className={styles.properties}>
<StreamProperty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ exports[`<BulkEditPanel /> should render 1`] = `
class="<removed-for-snapshot-test>"
>
<input
aria-checked="false"
class="<removed-for-snapshot-test>"
type="checkbox"
value=""
Expand Down
143 changes: 96 additions & 47 deletions airbyte-webapp/src/components/ui/Switch/Switch.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,63 @@
@use "scss/variables";
@use "scss/z-indices";

@mixin knob-transform($position, $small: false) {
@mixin knob-transform($position, $size) {
@if $position == left {
transform: translateX(0);
} @else if $small {
transform: translateX(10px);
} @else if $position == middle {
@if $size == lg {
transform: translateX(8px);
}
@if $size == sm {
transform: translateX(4.5px);
}
@if $size == xs {
transform: translateX(3.5px);
}
} @else {
transform: translateX(18px);
@if $size == lg {
transform: translateX(16px);
}
@if $size == sm {
transform: translateX(9px);
}
@if $size == xs {
transform: translateX(7px);
}
}
}

@mixin all-sizes-knob-transform($position) {
&.sizeLg::before {
@include knob-transform($position, lg);
}

&.sizeSm::before {
@include knob-transform($position, sm);
}

&.sizeXs::before {
@include knob-transform($position, xs);
}
}

.switch {
position: relative;
display: inline-block;
width: 42px;
height: 26px;

&.small {
width: 28px;
height: 18px;
&.sizeLg {
width: 42px;
height: 26px;
}

&.sizeSm {
width: 25px;
height: 16px;
}

&.sizeXs {
width: 17px;
height: 10px;
}

.slider {
Expand All @@ -33,25 +71,39 @@
background: colors.$grey-100;
transition: variables.$transition;
border-radius: variables.$border-radius-pill;
border: 1px solid colors.$grey-200;

&.small::before {
height: 16px;
width: 16px;
&.sizeLg {
border: 1px solid colors.$grey-200;
}

&.sizeLg::before {
width: 24px;
height: 24px;
left: 0;
top: 0;
}

&.sizeSm::before {
height: 14px;
width: 14px;
top: 1px;
left: 1px;
}

&.sizeXs::before {
width: 8px;
height: 8px;
top: 1px;
left: 1px;
}

&::before {
position: absolute;
z-index: z-indices.$switchSliderBefore;
content: "";
height: 24px;
width: 24px;
left: -1px;
top: -1px;
background: colors.$white;
transition: variables.$transition;
border-radius: 50%;
border: 1px solid colors.$grey-200;
}
}

Expand All @@ -60,55 +112,52 @@
width: 0;
height: 0;

&:checked + .slider {
background-color: colors.$blue;

&::before {
@include knob-transform(right, false);
&[aria-checked="true"] + .slider {
&.variantDefault {
background-color: colors.$blue;
}

&.small::before {
@include knob-transform(right, true);
&.variantStrongBlue {
background-color: colors.$blue-700;
}

@include all-sizes-knob-transform(right);

&.loading {
background-image: url("./ProgressReverse.svg");

&::before {
@include knob-transform(left, false);
}

&.small::before {
@include knob-transform(left, true);
}
@include all-sizes-knob-transform(left);
}
}

&:not(:checked) + .slider {
&::before {
@include knob-transform(left, false);
}

&.small::before {
@include knob-transform(left, true);
}
&[aria-checked="false"] + .slider {
@include all-sizes-knob-transform(left);

&.loading {
background-image: url("./Progress.svg");
@include all-sizes-knob-transform(right);
}
}

&::before {
@include knob-transform(right, false);
}
&[aria-checked="mixed"] + .slider {
&.variantDefault {
background-color: colors.$blue-200;
}

&.small::before {
@include knob-transform(right, true);
}
&.loading {
background-color: colors.$blue-100;
background-image: url("./ProgressReverse.svg");
}

@include all-sizes-knob-transform(middle);
}

&:disabled + .slider {
opacity: 0.5;
cursor: auto;

&.loading {
opacity: 1;
}
}
}
}
2 changes: 1 addition & 1 deletion airbyte-webapp/src/components/ui/Switch/Switch.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ const Template: ComponentStory<typeof Switch> = (args) => <Switch {...args} />;
export const SwitchControl = Template.bind({});
SwitchControl.args = {
checked: false,
small: false,
size: "sm",
loading: false,
};
35 changes: 29 additions & 6 deletions airbyte-webapp/src/components/ui/Switch/Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,52 @@ import React from "react";

import styles from "./Switch.module.scss";

interface SwitchProps extends React.InputHTMLAttributes<HTMLInputElement> {
small?: boolean;
type SwitchSize = "lg" | "sm" | "xs";

type SwitchVariant = "default" | "strong-blue";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we want to bother with it, but I saw (I think Edmundo) use const enum which lets us use in-code values but transpiles out to strings!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For props, we agreed to keep using types or unions.


interface SwitchProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
indeterminate?: boolean;
loading?: boolean;
size?: SwitchSize;
variant?: SwitchVariant;
}

export const Switch: React.FC<SwitchProps> = ({ loading, small, checked, value, ...props }) => {
export const Switch: React.FC<SwitchProps> = ({
checked,
disabled,
indeterminate,
loading,
size = "lg",
value,
variant = "default",
...props
}) => {
const labelStyle = classnames(styles.switch, {
[styles.small]: small,
[styles.sizeLg]: size === "lg",
[styles.sizeSm]: size === "sm",
[styles.sizeXs]: size === "xs",
[styles.loading]: loading,
});
const spanStyle = classnames(styles.slider, {
[styles.small]: small,
[styles.sizeLg]: size === "lg",
[styles.sizeSm]: size === "sm",
[styles.sizeXs]: size === "xs",
[styles.variantDefault]: variant === "default",
[styles.variantStrongBlue]: variant === "strong-blue",
[styles.indeterminate]: indeterminate,
[styles.loading]: loading,
});

return (
<label className={labelStyle}>
<input
{...props}
aria-checked={indeterminate ? "mixed" : checked}
className={styles.switchInput}
type="checkbox"
value={value}
disabled={loading || props.disabled}
disabled={loading || disabled}
checked={checked || !!value}
/>
<span className={spanStyle} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ exports[`ConnectionReplicationTab should render 1`] = `
class="<removed-for-snapshot-test>"
>
<input
aria-checked="true"
checked=""
class="<removed-for-snapshot-test>"
data-testid="pokemon-stream-sync-switch"
Expand Down