Skip to content

Commit 8f2cf21

Browse files
author
Tim Roes
authored
🪟 🎉 Empty connection page redesign (#19366)
* Empty connection page * Cleanup * Resolve TODOs * Cleanup Illustration * Aria labels for buttons * Fix minor issues * Add comments * Fix alignment issue * Fix missing keys * Parallelize loading * Link up to LaunchDarkly * Extract round functionality * Fix border radius * Renamed to mixins * Extract type * Forgotten cast
1 parent 43feacc commit 8f2cf21

File tree

21 files changed

+642
-47
lines changed

21 files changed

+642
-47
lines changed

airbyte-webapp/src/components/common/EmptyResourceListView/EmptyResourceListView.module.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,3 @@
4343
left: 0;
4444
}
4545
}
46-
47-
.footer {
48-
margin-top: variables.$spacing-xl;
49-
}

airbyte-webapp/src/components/common/EmptyResourceListView/EmptyResourceListView.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@ interface EmptyResourceListViewProps {
1212
buttonLabel: string;
1313
resourceType: "connections" | "destinations" | "sources";
1414
onCreateClick: () => void;
15-
footer?: React.ReactNode;
1615
}
1716

1817
export const EmptyResourceListView: React.FC<EmptyResourceListViewProps> = ({
1918
resourceType,
2019
onCreateClick,
2120
buttonLabel,
22-
footer,
2321
}) => {
2422
const { headingMessageId, singularResourceType } = useMemo(() => {
2523
const singularResourceType = resourceType.substring(0, resourceType.length - 1);
@@ -52,7 +50,6 @@ export const EmptyResourceListView: React.FC<EmptyResourceListViewProps> = ({
5250
<Button onClick={onCreateClick} size="lg" data-id={`new-${singularResourceType}`}>
5351
{buttonLabel}
5452
</Button>
55-
{footer && <div className={styles.footer}>{footer}</div>}
5653
</div>
5754
);
5855
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@use "scss/colors";
2+
3+
:export {
4+
darkBlue: colors.$dark-blue-900;
5+
logoColor: colors.$blue-400;
6+
gradientOrange: colors.$orange-400;
7+
gradientBlue: colors.$blue-400;
8+
dotColor: colors.$blue-400;
9+
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import styles from "./AirbyteIllustration.module.scss";
2+
3+
export type HighlightIndex = 0 | 1 | 2 | 3;
4+
5+
interface AirbyteIllustrationProps {
6+
sourceHighlighted: HighlightIndex;
7+
destinationHighlighted: HighlightIndex;
8+
}
9+
10+
const regularPath = {
11+
stroke: styles.darkBlue,
12+
strokeWidth: 1.5,
13+
strokeDasharray: "3 6",
14+
opacity: 0.2,
15+
};
16+
17+
const highlightedSource = {
18+
stroke: "url(#highlightedSource)",
19+
strokeWidth: 5,
20+
};
21+
22+
const highlightedDestination = {
23+
stroke: "url(#highlightedDestination)",
24+
strokeWidth: 5,
25+
};
26+
27+
export const AirbyteIllustration: React.FC<AirbyteIllustrationProps> = ({
28+
sourceHighlighted,
29+
destinationHighlighted,
30+
}) => (
31+
<svg width="492" height="318" viewBox="0 0 492 318" fill="none" xmlns="http://www.w3.org/2000/svg">
32+
<path
33+
id="sourcePath0"
34+
d="M0 25H16.3176C38.7973 25 58.5134 40.0021 64.5074 61.668L80.4605 119.332C86.4545 140.998 106.171 156 128.65 156H179"
35+
strokeLinejoin="round"
36+
{...(sourceHighlighted === 0 ? highlightedSource : regularPath)}
37+
/>
38+
<path
39+
id="sourcePath1"
40+
d="M0 115H31.8265C46.1566 115 59.798 121.149 69.2887 131.885L75.6792 139.115C85.1699 149.851 98.8113 156 113.141 156H179"
41+
strokeLinejoin="round"
42+
{...(sourceHighlighted === 1 ? highlightedSource : regularPath)}
43+
/>
44+
<path
45+
id="sourcePath2"
46+
d="M0 202H30.1023C45.422 202 59.8962 194.977 69.3771 182.943L75.5908 175.057C85.0717 163.023 99.5459 156 114.866 156H179"
47+
strokeLinejoin="round"
48+
{...(sourceHighlighted === 2 ? highlightedSource : regularPath)}
49+
/>
50+
<path
51+
id="sourcePath3"
52+
d="M0 288H16.2406C38.7565 288 58.495 272.95 64.4563 251.238L80.5116 192.762C86.4729 171.049 106.211 156 128.727 156H179"
53+
strokeLinejoin="round"
54+
{...(sourceHighlighted === 3 ? highlightedSource : regularPath)}
55+
/>
56+
{/* We need to render the highlighted element once more, so it will overlap the other dashed lines (since there's no z-index in SVG) */}
57+
<use xlinkHref={`#sourcePath${sourceHighlighted}`} />
58+
59+
<path
60+
id="destinationPath0"
61+
d="M492 25H475.682C453.203 25 433.487 40.0021 427.493 61.668L411.539 119.332C405.545 140.998 385.829 156 363.35 156H313"
62+
strokeLinejoin="round"
63+
{...(destinationHighlighted === 0 ? highlightedDestination : regularPath)}
64+
/>
65+
<path
66+
id="destinationPath1"
67+
d="M492 115H460.173C445.843 115 432.202 121.149 422.711 131.885L416.321 139.115C406.83 149.851 393.189 156 378.859 156H313"
68+
{...(destinationHighlighted === 1 ? highlightedDestination : regularPath)}
69+
strokeLinejoin="round"
70+
/>
71+
<path
72+
id="destinationPath2"
73+
d="M492 202H461.898C446.578 202 432.104 194.977 422.623 182.943L416.409 175.057C406.928 163.023 392.454 156 377.134 156H313"
74+
strokeLinejoin="round"
75+
{...(destinationHighlighted === 2 ? highlightedDestination : regularPath)}
76+
/>
77+
<path
78+
id="destinationPath3"
79+
d="M492 288H475.759C453.243 288 433.505 272.95 427.544 251.238L411.488 192.762C405.527 171.049 385.789 156 363.273 156H313"
80+
strokeLinejoin="round"
81+
{...(destinationHighlighted === 3 ? highlightedDestination : regularPath)}
82+
/>
83+
{/* We need to render the highlighted element once more, so it will overlap the other dashed lines (since there's no z-index in SVG) */}
84+
<use xlinkHref={`#destinationPath${destinationHighlighted}`} />
85+
86+
<circle cx="0" cy="0" r="6" fill={styles.dotColor} stroke="white" strokeWidth={2}>
87+
<animateMotion dur="3s" repeatCount="indefinite" rotate="auto">
88+
{/* Animate the dot along the current highlighted source path */}
89+
<mpath xlinkHref={`#sourcePath${sourceHighlighted}`} />
90+
</animateMotion>
91+
</circle>
92+
<circle cx="0" cy="0" r="6" fill={styles.dotColor} stroke="white" strokeWidth={2}>
93+
<animateMotion dur="3s" repeatCount="indefinite" rotate="auto" keyPoints="1;0" keyTimes="0;1" calcMode="linear">
94+
<mpath xlinkHref={`#destinationPath${destinationHighlighted}`} />
95+
</animateMotion>
96+
</circle>
97+
<g filter="url(#backgroundGradient)">
98+
<rect x="179" y="88" width="134" height="134" rx="48" fill="white" />
99+
<path
100+
fillRule="evenodd"
101+
clipRule="evenodd"
102+
d="M235.109 128.384C242.704 119.85 255.281 117.347 265.621 122.273C279.359 128.818 284.371 145.49 276.891 158.351L260.065 187.25C259.125 188.865 257.577 190.043 255.763 190.526C253.948 191.009 252.015 190.757 250.387 189.826L270.759 154.831C276.185 145.497 272.556 133.398 262.595 128.635C255.122 125.061 245.986 126.846 240.465 132.993C237.42 136.367 235.71 140.724 235.653 145.255C235.596 149.785 237.196 154.183 240.155 157.632C240.687 158.251 241.259 158.834 241.869 159.378L229.976 179.845C229.511 180.645 228.892 181.346 228.154 181.908C227.416 182.471 226.574 182.883 225.675 183.123C224.776 183.362 223.839 183.423 222.917 183.302C221.995 183.182 221.105 182.882 220.3 182.42L233.21 160.201C231.356 157.546 230.024 154.567 229.285 151.42L221.374 165.063C220.433 166.678 218.886 167.856 217.071 168.339C215.257 168.822 213.324 168.57 211.696 167.638L232.155 132.447C233.007 131.002 233.996 129.641 235.109 128.384ZM258.722 139.585C263.649 142.411 265.351 148.695 262.5 153.586L242.881 187.246C241.941 188.861 240.394 190.039 238.579 190.522C236.765 191.005 234.831 190.753 233.204 189.822L251.42 158.484C249.959 158.179 248.581 157.562 247.383 156.676C246.185 155.79 245.195 154.657 244.48 153.354C243.766 152.052 243.345 150.61 243.247 149.13C243.148 147.65 243.374 146.167 243.909 144.782C244.444 143.397 245.276 142.144 246.346 141.109C247.416 140.074 248.7 139.283 250.108 138.789C251.516 138.295 253.016 138.11 254.503 138.247C255.99 138.385 257.43 138.841 258.722 139.585ZM251.586 145.911C251.249 146.168 250.966 146.488 250.754 146.854H250.753C250.432 147.405 250.284 148.037 250.326 148.672C250.368 149.306 250.598 149.914 250.988 150.418C251.378 150.923 251.91 151.301 252.516 151.505C253.122 151.709 253.776 151.731 254.394 151.566C255.013 151.401 255.568 151.058 255.99 150.58C256.412 150.102 256.682 149.511 256.766 148.881C256.849 148.25 256.743 147.609 256.46 147.039C256.176 146.469 255.729 145.995 255.175 145.677C254.807 145.466 254.4 145.329 253.979 145.274C253.558 145.219 253.129 145.247 252.719 145.356C252.308 145.465 251.923 145.654 251.586 145.911Z"
103+
fill={styles.logoColor}
104+
/>
105+
</g>
106+
<defs>
107+
<filter
108+
id="backgroundGradient"
109+
x="101"
110+
y="0"
111+
width="297"
112+
height="318"
113+
filterUnits="userSpaceOnUse"
114+
colorInterpolationFilters="sRGB"
115+
>
116+
<feFlood floodOpacity="0" result="BackgroundImageFix" />
117+
<feColorMatrix
118+
in="SourceAlpha"
119+
type="matrix"
120+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
121+
result="hardAlpha"
122+
/>
123+
<feMorphology radius="10" operator="erode" in="SourceAlpha" result="effect1_dropShadow_3082_52204" />
124+
<feOffset dy="13" />
125+
<feGaussianBlur stdDeviation="9" />
126+
<feColorMatrix type="matrix" values="0 0 0 0 0.101961 0 0 0 0 0.0980392 0 0 0 0 0.301961 0 0 0 0.17 0" />
127+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3082_52204" />
128+
<feColorMatrix
129+
in="SourceAlpha"
130+
type="matrix"
131+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
132+
result="hardAlpha"
133+
/>
134+
<feOffset dx="23" dy="-27" />
135+
<feGaussianBlur stdDeviation="30.5" />
136+
<feComposite in2="hardAlpha" operator="out" />
137+
<feColorMatrix type="matrix" values="0 0 0 0 0.984314 0 0 0 0 0.223529 0 0 0 0 0.372549 0 0 0 0.2 0" />
138+
<feBlend mode="normal" in2="effect1_dropShadow_3082_52204" result="effect2_dropShadow_3082_52204" />
139+
<feColorMatrix
140+
in="SourceAlpha"
141+
type="matrix"
142+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
143+
result="hardAlpha"
144+
/>
145+
<feOffset dx="-20" dy="2" />
146+
<feGaussianBlur stdDeviation="29" />
147+
<feComposite in2="hardAlpha" operator="out" />
148+
<feColorMatrix type="matrix" values="0 0 0 0 0.262745 0 0 0 0 0.231373 0 0 0 0 0.984314 0 0 0 0.3 0" />
149+
<feBlend mode="normal" in2="effect2_dropShadow_3082_52204" result="effect3_dropShadow_3082_52204" />
150+
<feColorMatrix
151+
in="SourceAlpha"
152+
type="matrix"
153+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
154+
result="hardAlpha"
155+
/>
156+
<feOffset dx="12" dy="23" />
157+
<feGaussianBlur stdDeviation="36.5" />
158+
<feComposite in2="hardAlpha" operator="out" />
159+
<feColorMatrix type="matrix" values="0 0 0 0 0.404861 0 0 0 0 0.854625 0 0 0 0 0.883333 0 0 0 0.41 0" />
160+
<feBlend mode="normal" in2="effect3_dropShadow_3082_52204" result="effect4_dropShadow_3082_52204" />
161+
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_3082_52204" result="shape" />
162+
</filter>
163+
<linearGradient id="highlightedSource" x1="490" y1="25" x2="5.00002" y2="115" gradientUnits="userSpaceOnUse">
164+
<stop stopColor={styles.gradientOrange} />
165+
<stop offset="1" stopColor={styles.gradientBlue} />
166+
</linearGradient>
167+
<linearGradient id="highlightedDestination" x1="492" y1="25" x2="-2" y2="115" gradientUnits="userSpaceOnUse">
168+
<stop stopColor={styles.gradientOrange} />
169+
<stop offset="1" stopColor={styles.gradientBlue} />
170+
</linearGradient>
171+
</defs>
172+
</svg>
173+
);
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
@use "scss/colors";
2+
@use "scss/variables";
3+
@use "scss/mixins";
4+
5+
.container {
6+
display: flex;
7+
gap: variables.$spacing-2xl;
8+
flex-direction: column;
9+
align-items: center;
10+
justify-content: center;
11+
height: 100%;
12+
}
13+
14+
.heading {
15+
color: colors.$dark-blue;
16+
17+
strong {
18+
color: colors.$blue;
19+
}
20+
}
21+
22+
.sourcesTitle,
23+
.destinationsTitle {
24+
color: colors.$dark-blue-400;
25+
text-align: center;
26+
cursor: help;
27+
28+
// Required to have the title centered (no matter it's length) above the buttons
29+
// without extending the size of this flex box, which would cause the buttons to be
30+
// unaligned with the illustration
31+
position: absolute;
32+
left: 50%;
33+
transform: translateX(-50%);
34+
white-space: nowrap;
35+
margin-top: -1.5 * variables.$spacing-xl;
36+
}
37+
38+
.sources,
39+
.destinations {
40+
display: flex;
41+
width: fit-content;
42+
flex-direction: column;
43+
position: relative;
44+
45+
// Manual spacing/margin to align with the illustration in the center
46+
gap: 12px;
47+
margin-top: -7px;
48+
}
49+
50+
.connectors {
51+
display: flex;
52+
margin-top: variables.$spacing-2xl;
53+
}
54+
55+
.connectorButton {
56+
@include mixins.shadow;
57+
58+
width: 100%;
59+
aspect-ratio: 1 / 1;
60+
border: none;
61+
padding: 18px;
62+
cursor: pointer;
63+
background: colors.$white;
64+
border-radius: variables.$border-radius-2xl;
65+
transition: box-shadow variables.$transition ease;
66+
67+
.destinations & {
68+
cursor: default;
69+
opacity: 0.7;
70+
}
71+
72+
&:hover,
73+
&:focus-visible {
74+
box-shadow: 0 10px 19px rgba(colors.$dark-blue-900, 0.16);
75+
}
76+
}
77+
78+
.connectorIcon,
79+
.moreIcon {
80+
width: 38px;
81+
height: 38px;
82+
}
83+
84+
.moreIcon {
85+
width: 28px;
86+
height: 28px;
87+
color: colors.$blue;
88+
}
89+
90+
.demoLink {
91+
color: colors.$dark-blue;
92+
93+
&:hover,
94+
&:focus {
95+
color: colors.$blue-400;
96+
}
97+
}
98+
99+
.footer {
100+
display: flex;
101+
flex-direction: column;
102+
gap: variables.$spacing-xl;
103+
}

0 commit comments

Comments
 (0)