Skip to content

Commit

Permalink
🪟 🐛 Update connection name - fix minor issues (#13550)
Browse files Browse the repository at this point in the history
* align text to center in edit mode

* fix firing onChange event twice

* fix wrong prop drilling

* fix issues:
- unable to delete last chart in input
- delete the whole name by 'select all' command
- trim entered names
- don't allow to save empty names

* remove debug from test

* remove unused import

* update tests due to changes in base Input component

* reorder default input data-testId in order to have ability to overwrite it

* fix e2e tests

* Improvements:
- replace "props: any" with right types
- rename 'addEnterEscFuncForInput' to more general and self-describable name

* replace styled components with scss modules

* add eslint-plugin-css-modules rules

* Fixes:
- turn on eslint css modules rule as error
- remove unused styles

* minor fix after master merge

* Revert "Fixes:"

This reverts commit d7fb68a.

* Revert "add eslint-plugin-css-modules rules"

This reverts commit 0333a70.

* apply suggested changes after PR review

* add updates due to changes in PR #13914

* add react-app-rewired to tweak webpack config and fix the css order issue

* remove test flag "CI=true"

* Revert "add react-app-rewired to tweak webpack config and fix the css order issue"

This reverts commit 81d7ba4
  • Loading branch information
dizel852 authored Jul 8, 2022
1 parent df24831 commit 1e808ec
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 148 deletions.
2 changes: 1 addition & 1 deletion airbyte-webapp-e2e-tests/cypress/commands/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const createTestConnection = (sourceName: string, destinationName: string

cy.wait("@discoverSchema");

cy.get("div[data-testid='connectionName']").type("Connection name");
cy.get("input[data-testid='connectionName']").type("Connection name");
cy.get("div[data-testid='schedule']").click();
cy.get("div[data-testid='Manual']").click();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const ConnectorName = styled.div`
margin-top: 1px;
color: #afafc1;
text-align: left;
word-wrap: break-word;
`;

const ConnectorCard = (props: Props) => {
Expand Down
11 changes: 11 additions & 0 deletions airbyte-webapp/src/components/base/Input/Input.test.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { fireEvent } from "@testing-library/react";

import { render } from "utils/testutils";

import { Input } from "./Input";
Expand Down Expand Up @@ -41,4 +43,13 @@ describe("<Input />", () => {
expect(getByTestId("input")).toHaveValue(value);
expect(getByRole("img", { hidden: true })).toHaveAttribute("data-icon", "eye-slash");
});

test("should trigger onChange once", async () => {
const onChange = jest.fn();
const { getByTestId } = await render(<Input onChange={onChange} />);
const inputEl = getByTestId("input");

fireEvent.change(inputEl, { target: { value: "one more test" } });
expect(onChange).toHaveBeenCalledTimes(1);
});
});
4 changes: 2 additions & 2 deletions airbyte-webapp/src/components/base/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,15 @@ const Input: React.FC<InputProps> = (props) => {
}, [inputRef, defaultFocus]);

return (
<InputContainer {...props} className={focused ? "input-container--focused" : undefined}>
<InputContainer className={focused ? "input-container--focused" : undefined}>
<InputComponent
data-testid="input"
{...props}
ref={inputRef}
type={type}
isPassword={isPassword}
onFocus={onInputFocusChange}
onBlur={onInputFocusChange}
data-testid="input"
/>
{isVisibilityButtonVisible ? (
<VisibilityButton
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
@use "../../../../../scss/colors";
@use "../../../../../scss/variables";

.container {
margin-top: variables.$spacing-m;
display: flex;
align-items: center;
justify-content: center;
}

.textContainer {
width: 650px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}

.nameContainer {
composes: textContainer;
background-color: colors.$beige-100;
border: variables.$border-thin solid colors.$beige-100;
border-radius: variables.$border-radius-sm;
padding: 0 variables.$spacing-xl;

div {
width: 100%;

h2 {
font-weight: 700;
font-size: 24px;
line-height: 29px;
text-align: center;
color: colors.$dark-blue-900;
margin: variables.$spacing-m;
word-wrap: break-word;
}
}

&:hover {
cursor: pointer;
border: variables.$border-thin solid colors.$blue;
background-color: colors.$blue-50;

& > .icon {
display: block;
}
}
}

.icon {
display: none;
position: absolute;
right: variables.$spacing-xl;
font-size: 18px;
color: colors.$blue;
}

.editingContainer {
composes: textContainer;
background-color: colors.$white;
border: variables.$border-thin solid colors.$blue;
border-radius: variables.$border-radius-sm;
}

.inputContainer {
height: 50px;
width: 100%;

div {
border-radius: variables.$border-radius-sm;
border: none;
box-shadow: none;
background-color: colors.$white !important; // fix style import order issue
}
}

.input {
border-radius: variables.$border-radius-sm;
background-color: colors.$white;
font-size: 24px !important; // fix style import order issue
height: 50px;
text-align: center;
}
Original file line number Diff line number Diff line change
@@ -1,119 +1,31 @@
import { faPenToSquare } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, { ChangeEvent, useState } from "react";
import styled from "styled-components";

import { Input } from "components";

import { buildConnectionUpdate } from "core/domain/connection";
import { WebBackendConnectionRead } from "core/request/AirbyteClient";
import { useUpdateConnection } from "hooks/services/useConnectionHook";
import addEnterEscFuncForInput from "utils/addEnterEscFuncForInput";
import withKeystrokeHandler from "utils/withKeystrokeHandler";

interface Props {
import styles from "./ConnectionName.module.scss";

interface ConnectionNameProps {
connection: WebBackendConnectionRead;
}

const MainContainer = styled.div`
margin-top: 10px;
display: flex;
align-items: center;
justify-content: center;
`;

const Icon = styled(FontAwesomeIcon)`
display: none;
position: absolute;
right: 20px;
font-size: 18px;
color: ${({ theme }) => theme.primaryColor};
`;

const NameContainer = styled.div`
width: 650px;
background-color: rgba(255, 235, 215, 0.4);
display: flex;
align-items: center;
position: relative;
padding: 0 20px;
border-radius: 8px;
border: 1px solid rgba(255, 235, 215, 0.4);
&:hover {
cursor: pointer;
border: ${({ theme }) => `1px solid ${theme.primaryColor}`};
background-color: ${({ theme }) => theme.primaryColor12};
}
&:hover ${Icon} {
display: block;
}
`;

const EditingContainer = styled.div`
width: 650px;
display: flex;
background-color: white;
justify-content: center;
align-items: center;
border-radius: 8px;
border: ${({ theme }) => `1px solid ${theme.primaryColor}`};
`;

const InputContainer = styled.div`
height: 50px;
width: 100%;
div {
border-radius: 8px;
border: none;
box-shadow: none;
background-color: white !important;
}
`;

const Name = styled.div`
flex-grow: 1;
`;

const H2 = styled.h2`
font-weight: 700;
font-size: 24px;
line-height: 29px;
text-align: center;
color: #1a194d;
margin: 10px;
`;

const StyledInput = styled(Input)`
border-radius: 8px;
background-color: white;
font-size: 24px;
height: 50px;
div {
border: none;
}
`;

const InputWithKeystroke = addEnterEscFuncForInput(StyledInput);

const ConnectionName: React.FC<Props> = ({ connection }) => {
const InputWithKeystroke = withKeystrokeHandler(Input);

const ConnectionName: React.FC<ConnectionNameProps> = ({ connection }) => {
const { name } = connection;
const [editingState, setEditingState] = useState(false);
const [loading, setLoading] = useState(false);
const [connectionName, setConnectionName] = useState(connection.name);
const [connectionName, setConnectionName] = useState<string | undefined>(connection.name);
const [connectionNameBackup, setConnectionNameBackup] = useState(connectionName);
const { mutateAsync: updateConnection } = useUpdateConnection();

const setEditing = () => {
setEditingState(true);
};

const inputChange = (event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.currentTarget;
if (value) {
setConnectionName(event.currentTarget.value);
}
};
const inputChange = ({ currentTarget: { value } }: ChangeEvent<HTMLInputElement>) => setConnectionName(value);

const onEscape: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
event.stopPropagation();
Expand All @@ -131,46 +43,55 @@ const ConnectionName: React.FC<Props> = ({ connection }) => {
};

const updateConnectionAsync = async () => {
// Update only when the name is changed
if (connection.name !== connectionName) {
const connectionNameTrimmed = connectionName?.trim();
if (!connectionNameTrimmed || connection.name === connectionNameTrimmed) {
setConnectionName(connectionNameBackup);
setEditingState(false);
return;
}

try {
setLoading(true);
await updateConnection(
buildConnectionUpdate(connection, {
name: connectionName,
})
);

await updateConnection(buildConnectionUpdate(connection, { name: connectionNameTrimmed }));

setConnectionName(connectionNameTrimmed);
setConnectionNameBackup(connectionNameTrimmed);
} catch (e) {
console.error(e.message);
setConnectionName(connectionNameBackup);
} finally {
setLoading(false);
}

setEditingState(false);
};

return (
<MainContainer>
{!editingState && (
<NameContainer onClick={setEditing}>
<Name>
<H2>{name}</H2>
</Name>
<Icon icon={faPenToSquare} />
</NameContainer>
)}
{editingState && (
<EditingContainer>
<InputContainer>
<div className={styles.container}>
{editingState ? (
<div className={styles.editingContainer}>
<div className={styles.inputContainer} onBlur={onBlur}>
<InputWithKeystroke
className={styles.input}
value={connectionName}
onChange={inputChange}
onBlur={onBlur}
onEscape={onEscape}
onEnter={onEnter}
disabled={loading}
defaultFocus
/>
</InputContainer>
</EditingContainer>
</div>
</div>
) : (
<div className={styles.nameContainer} onClick={() => setEditingState(true)}>
<div>
<h2>{name}</h2>
</div>
<FontAwesomeIcon className={styles.icon} icon={faPenToSquare} />
</div>
)}
</MainContainer>
</div>
);
};

Expand Down
3 changes: 3 additions & 0 deletions airbyte-webapp/src/scss/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ $transition: 0.3s;
$border-thin: 1px;
$border-thick: 2px;

$border-radius-sm: 8px;
$border-radius-m: 10px;

$spacing-xs: 3px;
$spacing-sm: 5px;
$spacing-m: 10px;
Expand Down
22 changes: 0 additions & 22 deletions airbyte-webapp/src/utils/addEnterEscFuncForInput.tsx

This file was deleted.

Loading

0 comments on commit 1e808ec

Please sign in to comment.