Merge pull request #14103 from appsmithorg/Fix/Mismatch-with-design-in-Sample-Datasource-Card

fix: UI inconsistencies in Datasource card and page
This commit is contained in:
ChandanBalajiBP 2022-06-29 17:32:43 +05:30 committed by GitHub
commit be626a132e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 397 additions and 98 deletions

View File

@ -0,0 +1,190 @@
import HomePage from "../../../../locators/HomePage";
const pages = require("../../../../locators/Pages.json");
describe("Validate Datasource Panel Styles", function() {
const backgroundColorGray900 = "rgb(25, 25, 25)";
const backgroundColorGray700 = "rgb(87, 87, 87)";
const backgroundColorGray1 = "rgb(250, 250, 250)";
const backgroundColorGray2 = "rgb(240, 240, 240)";
before(() => {
//Navigate to datasource pane
cy.get(pages.addEntityAPI)
.last()
.should("be.visible")
.click({ force: true });
cy.createMockDatasource();
cy.get(pages.integrationCreateNew).click();
});
it("1. Tabs style", function() {
//Check gap between tab items
cy.get("[data-cy=t--datasource-tab] .react-tabs__tab-list").should(
"have.css",
"gap",
"32px",
);
//Check styles of tab titles.
cy.get(".tab-title")
.should("have.css", "font-size", "16px")
.and("have.css", "font-weight", "400")
.and("have.css", "line-height", "24px")
.and("have.css", "letter-spacing", "-0.24px");
//Check selected tab item style
cy.get(".react-tabs__tab--selected .tab-title").should(
"have.css",
"color",
backgroundColorGray900,
);
});
it("2. Mock datasource card design", () => {
cy.get(pages.integrationCreateNew).click();
//Card container style
cy.datasourceCardContainerStyle(".t--mock-datasource-list");
//Datasource card
cy.datasourceCardStyle(".t--mock-datasource");
//Description
cy.mockDatasourceDescriptionStyle(
"[data-testid=mockdatasource-description]",
);
//mock datasource image
cy.datasourceImageStyle("[data-testid=mock-datasource-image]");
//header text
cy.datasourceContentWrapperStyle(".t--datasource-name");
//Icon wrapper
cy.datasourceIconWrapperStyle("[data-testid=mock-datasource-icon-wrapper]");
//Name wrapper
cy.get("[data-testid=mock-datasource-name-wrapper]")
.should("have.css", "display", "flex")
.and("have.css", "flex-direction", "column");
//Name
cy.datasourceNameStyle("[data-testid=mockdatasource-name]");
});
it("3. Database datasource card design", () => {
cy.get(pages.integrationCreateNew).click();
//Card container style
cy.datasourceCardContainerStyle(
"[data-testid=database-datasource-card-container]",
);
//Datasource card
cy.datasourceCardStyle("[data-testid=database-datasource-card]");
//mock datasource image
cy.datasourceImageStyle("[data-testid=database-datasource-image]");
//header text
cy.datasourceContentWrapperStyle(
"[data-testid=database-datasource-content-wrapper]",
);
//Icon wrapper
cy.datasourceIconWrapperStyle(
"[data-testid=database-datasource-content-wrapper] .dataSourceImageWrapper",
);
//Name
cy.datasourceNameStyle(
"[data-testid=database-datasource-content-wrapper] .textBtn",
);
});
it("4. New API datasource card design", () => {
cy.get(pages.integrationCreateNew).click();
//Card container style
cy.datasourceCardContainerStyle(
"[data-testid=newapi-datasource-card-container]",
);
//Datasource card
cy.datasourceCardStyle(".t--createBlankApiCard");
//Datasource image
cy.datasourceImageStyle(".content-icon");
//Header text
cy.datasourceContentWrapperStyle(
"[data-testid=newapi-datasource-content-wrapper]",
);
//Icon wrapper
cy.datasourceIconWrapperStyle(".content-icon-wrapper");
//Name
cy.datasourceNameStyle(".t--createBlankApiCard .textBtn");
});
it("Datasource title font size", () => {
cy.get(".t--integrationsHomePage").should("have.css", "font-size", "20px");
});
it("5. Action button icon placement", () => {
//Navigate to Active tab
cy.get(pages.integrationActiveTab).click({ force: true });
//Icon should be placed left to the text.
cy.get(".t--create-query .t--left-icon");
});
it("6. Datasource Active card styles", () => {
//Active card wrapper
cy.get(".t--datasource")
.should("have.css", "padding", "15px")
.and("have.css", "cursor", "pointer")
.realHover()
.should("have.css", "background-color", backgroundColorGray1);
cy.get("[data-testid=active-datasource-image]")
.should("have.css", "height", "18px")
.and("have.css", "max-width", "100%");
cy.get("[data-testid=active-datasource-icon-wrapper]")
.should("have.css", "background-color", backgroundColorGray2)
.and("have.css", "width", "34px")
.and("have.css", "height", "34px")
.and("have.css", "border-radius", "50%")
.and("have.css", "display", "flex")
.and("have.css", "align-items", "center");
//Name
cy.datasourceNameStyle("[data-testid=active-datasource-name]");
//Queries
cy.get("[data-testid=active-datasource-queries]")
.should("have.css", "display", "flex")
.and("have.css", "margin", "4px 0px");
//Buttons wrapper
cy.get(".t--datasource-name .action-wrapper")
.should("have.css", "gap", "10px")
.and("have.css", "display", "flex")
.and("have.css", "align-items", "center");
});
it("7. Collapse component styles", () => {
//Collapse wrapper
cy.get("[data-testid=datasource-collapse-wrapper]")
.should("have.css", "color", backgroundColorGray700)
.and("have.css", "display", "flex")
.and("have.css", "gap", "8px")
.and("have.css", "align-items", "center");
//Collapse icon
cy.get("[data-testid=datasource-collapse-icon] svg")
.invoke("attr", "data-icon")
.should("eq", "arrow-right");
cy.get("[data-testid=datasource-collapse-icon] svg")
.invoke("attr", "fill")
.should("eq", "#4B4848");
cy.get("[data-testid=datasource-collapse-icon] svg")
.invoke("attr", "width")
.should("eq", "12");
});
after(() => {
//Delete Datasource
cy.get(".t--datasource-menu-option")
.eq(0)
.click();
cy.get(".t--datasource-option-delete").click();
cy.get(".t--datasource-option-delete").click();
//Delete Application
cy.get(HomePage.applicationName).click();
cy.get(".t--application-edit-menu li")
.contains("Delete Application")
.click();
cy.get(".t--application-edit-menu li")
.contains("Are you sure?")
.click();
});
});

View File

@ -8,6 +8,11 @@ const datasourceEditor = require("../locators/DatasourcesEditor.json");
const datasourceFormData = require("../fixtures/datasources.json");
const explorer = require("../locators/explorerlocators.json");
const backgroundColorBlack = "rgb(0, 0, 0)";
const backgroundColorGray1 = "rgb(250, 250, 250)";
const backgroundColorGray2 = "rgb(240, 240, 240)";
const backgroundColorGray8 = "rgb(113, 110, 110)";
export const initLocalstorage = () => {
cy.window().then((window) => {
window.localStorage.setItem("ShowCommentsButtonToolTip", "");
@ -399,3 +404,69 @@ Cypress.Commands.add("fillMongoDatasourceFormWithURI", () => {
Cypress.Commands.add("ReconnectDatasource", (datasource) => {
cy.xpath(`//span[text()='${datasource}']`).click();
});
Cypress.Commands.add("createMockDatasource", () => {
cy.get(".t--mock-datasource")
.contains("Users")
.click();
});
Cypress.Commands.add("datasourceCardContainerStyle", (tag) => {
cy.get(tag)
.should("have.css", "min-width", "150px")
.and("have.css", "border-radius", "4px")
.and("have.css", "align-items", "center");
});
Cypress.Commands.add("datasourceCardStyle", (tag) => {
cy.get(tag)
.should("have.css", "display", "flex")
.and("have.css", "justify-content", "space-between")
.and("have.css", "align-items", "center")
.and("have.css", "height", "64px")
.realHover()
.should("have.css", "background-color", backgroundColorGray1)
.and("have.css", "cursor", "pointer");
});
Cypress.Commands.add("datasourceImageStyle", (tag) => {
cy.get(tag)
.should("have.css", "height", "28px")
.and("have.css", "max-width", "100%");
});
Cypress.Commands.add("datasourceContentWrapperStyle", (tag) => {
cy.get(tag)
.should("have.css", "display", "flex")
.and("have.css", "align-items", "center")
.and("have.css", "gap", "13px")
.and("have.css", "padding-left", "13.5px");
});
Cypress.Commands.add("datasourceIconWrapperStyle", (tag) => {
cy.get(tag)
.should("have.css", "background-color", backgroundColorGray2)
.and("have.css", "width", "48px")
.and("have.css", "height", "48px")
.and("have.css", "border-radius", "50%")
.and("have.css", "display", "flex")
.and("have.css", "align-items", "center");
});
Cypress.Commands.add("datasourceNameStyle", (tag) => {
cy.get(tag)
.should("have.css", "color", backgroundColorBlack)
.and("have.css", "font-size", "16px")
.and("have.css", "font-weight", "400")
.and("have.css", "line-height", "24px")
.and("have.css", "letter-spacing", "-0.24px");
});
Cypress.Commands.add("mockDatasourceDescriptionStyle", (tag) => {
cy.get(tag)
.should("have.css", "color", backgroundColorGray8)
.and("have.css", "font-size", "13px")
.and("have.css", "font-weight", "400")
.and("have.css", "line-height", "17px")
.and("have.css", "letter-spacing", "-0.24px");
});

View File

@ -41,6 +41,8 @@ const TabsWrapper = styled.div<{
flex-direction: ${(props) => (!!props.vertical ? "column" : "row")};
align-items: ${(props) => (!!props.vertical ? "stretch" : "center")};
border-bottom: none;
gap: ${(props) =>
!props.vertical ? `${props.theme.spaces[12] + 2}px` : 0};
color: ${(props) => props.theme.colors.tabs.normal};
path {
fill: ${(props) => props.theme.colors.tabs.icon};
@ -74,9 +76,7 @@ const TabsWrapper = styled.div<{
justify-content: center;
border-color: transparent;
position: relative;
padding: 0px 3px;
margin-right: ${(props) =>
!props.vertical ? `${props.theme.spaces[12] - 3}px` : 0};
padding: 0;
${(props) =>
props.responseViewer &&
@ -134,10 +134,10 @@ const TabsWrapper = styled.div<{
`;
export const TabTitle = styled.span<{ responseViewer?: boolean }>`
font-size: ${(props) => props.theme.typography.h5.fontSize}px;
font-weight: ${(props) => props.theme.typography.h5.fontWeight};
line-height: ${(props) => props.theme.typography.h5.lineHeight - 3}px;
letter-spacing: ${(props) => props.theme.typography.h5.letterSpacing}px;
font-size: ${(props) => props.theme.typography.h4.fontSize}px;
font-weight: ${(props) => props.theme.fontWeights[1]};
line-height: ${(props) => props.theme.spaces[11]}px;
letter-spacing: ${(props) => props.theme.typography.h4.letterSpacing}px;
margin: 0;
display: flex;
align-items: center;
@ -215,18 +215,16 @@ const TabTitleWrapper = styled.div<{
props.selected
? `
background-color: transparent;
color: ${props.theme.colors.tabs.hover};
color: var(--appsmith-color-black-900);
.${Classes.ICON} {
svg {
fill: ${props.theme.colors.tabs.icon};
path {
fill: ${props.theme.colors.tabs.icon};
fill: var(--appsmith-color-black-900)
}
}
}
.tab-title {
font-weight: 700;
${props.responseViewer &&
`
font-weight: normal;

View File

@ -5,16 +5,16 @@ import { Collapse, Icon } from "@blueprintjs/core";
const CollapseWrapper = styled.div`
position: relative;
margin-top: ${(props) => props.theme.spaces[3]}px;
.collapse-title {
color: ${Colors.TROUT_DARK};
color: ${Colors.GRAY_700};
letter-spacing: 0.04em;
text-transform: uppercase;
font-weight: 500;
font-size: 12px;
line-height: 16px;
display: flex;
gap: 4px;
align-items: center;
gap: 8px;
cursor: pointer;
/* justify-content: space-between; */
.icon {
@ -50,14 +50,17 @@ function CollapseComponent(props: {
<CollapseWrapper>
<div
className="collapse-title"
data-testid="datasource-collapse-wrapper"
onClick={handleIsOpen}
style={props.titleStyle}
>
{props.title}
<Icon
className={`icon ${!open ? "collapse" : ""}`}
icon="chevron-down"
iconSize={16}
className={`icon ${open ? "collapse" : ""}`}
color="#4B4848"
data-testid="datasource-collapse-icon"
icon="arrow-right"
iconSize={12}
/>
</div>
<Collapse isOpen={open} keepChildrenMounted>

View File

@ -1,7 +1,7 @@
import React, { useCallback, useState } from "react";
import { Action, ApiActionConfig, PluginType } from "entities/Action";
import styled from "styled-components";
import Button from "components/ads/Button";
import Button, { IconPositions } from "components/ads/Button";
import { createNewApiName, createNewQueryName } from "utils/AppsmithUtils";
import { Toaster } from "components/ads/Toast";
import { ERROR_ADD_API_INVALID_URL } from "@appsmith/constants/messages";
@ -100,6 +100,7 @@ function NewActionButton(props: NewActionButtonProps) {
<ActionButton
className="t--create-query"
icon="plus"
iconPosition={IconPositions.left}
isLoading={isSelected || props.isLoading}
onClick={createQueryAction}
text={pluginType === PluginType.DB ? "New Query" : "New API"}

View File

@ -15,7 +15,6 @@ import {
const QueryHomePage = styled.div`
${thinScrollbar};
padding: 5px;
overflow: auto;
display: flex;
flex-direction: column;

View File

@ -42,15 +42,15 @@ import {
import { debounce } from "lodash";
const Wrapper = styled.div`
padding: 18px;
padding: 15px;
/* margin-top: 18px; */
cursor: pointer;
&:hover {
background: ${Colors.Gallery};
background-color: ${Colors.GREY_1};
.bp3-collapse-body {
background: ${Colors.Gallery};
background-color: ${Colors.GREY_1};
}
}
`;
@ -72,8 +72,19 @@ const MenuWrapper = styled.div`
`;
const DatasourceImage = styled.img`
height: 24px;
height: 18px;
width: auto;
margin: 0 auto;
max-width: 100%;
`;
const DatasourceIconWrapper = styled.div`
width: 34px;
height: 34px;
border-radius: 50%;
background: ${Colors.GREY_2};
display: flex;
align-items: center;
`;
const GenerateTemplateButton = styled(Button)`
@ -89,9 +100,11 @@ const GenerateTemplateButton = styled(Button)`
`;
const DatasourceName = styled.span`
margin-left: 10px;
color: ${Colors.BLACK};
font-size: 16px;
font-weight: 500;
font-weight: 400;
line-height: 24px;
letter-spacing: -0.24px;
`;
const DatasourceCardHeader = styled.div`
@ -105,6 +118,7 @@ const DatasourceNameWrapper = styled.div`
flex-direction: row;
align-items: center;
display: flex;
gap: 13px;
`;
const DatasourceInfo = styled.div`
@ -114,13 +128,14 @@ const DatasourceInfo = styled.div`
const Queries = styled.div`
color: ${Colors.DOVE_GRAY};
font-size: 14px;
display: inline-block;
margin-top: 11px;
display: flex;
margin: 4px 0;
`;
const ButtonsWrapper = styled.div`
display: flex;
gap: 10px;
align-items: center;
`;
const MoreOptionsContainer = styled.div`
@ -251,14 +266,21 @@ function DatasourceCard(props: DatasourceCardProps) {
<DatasourceCardHeader className="t--datasource-name">
<div style={{ flex: 1 }}>
<DatasourceNameWrapper>
<DatasourceImage
alt="Datasource"
className="dataSourceImage"
src={pluginImages[datasource.pluginId]}
/>
<DatasourceName>{datasource.name}</DatasourceName>
<DatasourceIconWrapper data-testid="active-datasource-icon-wrapper">
<DatasourceImage
alt="Datasource"
data-testid="active-datasource-image"
src={pluginImages[datasource.pluginId]}
/>
</DatasourceIconWrapper>
<DatasourceName data-testid="active-datasource-name">
{datasource.name}
</DatasourceName>
</DatasourceNameWrapper>
<Queries className={`t--queries-for-${plugin.type}`}>
<Queries
className={`t--queries-for-${plugin.type}`}
data-testid="active-datasource-queries"
>
{queriesWithThisDatasource
? `${queriesWithThisDatasource} ${QUERY} on this page`
: "No query in this application is using this datasource"}
@ -293,7 +315,7 @@ function DatasourceCard(props: DatasourceCardProps) {
target={
<MoreOptionsContainer>
<Icon
fillColor={Colors.GRAY2}
fillColor={Colors.GREY_8}
name="comment-context-menu"
size={IconSize.XXXL}
/>

View File

@ -37,17 +37,15 @@ const DatasourceHomePage = styled.div`
.textBtn {
justify-content: center;
text-align: center;
color: #2e3d49;
font-weight: 500;
color: ${Colors.BLACK};
font-weight: 400;
text-decoration: none !important;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
font-size: 16px;
line-height: 24px;
letter-spacing: -0.17px;
letter-spacing: -0.24px;
margin: 0;
}
`;
@ -68,20 +66,17 @@ const DatasourceCard = styled.div`
justify-content: space-between;
height: 64px;
&:hover {
background: ${Colors.Gallery};
background: ${Colors.GREY_1};
cursor: pointer;
}
.dataSourceImageWrapper {
width: 40px;
height: 40px;
padding: 6px 0;
border-radius: 20px;
margin: 0 8px;
background: #f0f0f0;
width: 48px;
height: 48px;
border-radius: 50%;
background: ${Colors.GREY_2};
display: flex;
align-items: center;
.dataSourceImage {
height: 28px;
width: auto;
@ -105,6 +100,8 @@ const DatasourceCard = styled.div`
const DatasourceContentWrapper = styled.div`
display: flex;
align-items: center;
gap: 13px;
padding-left: 13.5px;
`;
interface DatasourceHomeScreenProps {
@ -189,11 +186,11 @@ class DatasourceHomeScreen extends React.Component<Props> {
return (
<DatasourceHomePage>
<DatasourceCardsContainer>
<DatasourceCardsContainer data-testid="database-datasource-card-container">
{plugins.map((plugin, idx) => {
return (
<DatasourceCard
className="eachDatasourceCard"
data-testid="database-datasource-card"
key={`${plugin.id}_${idx}`}
onClick={() => {
AnalyticsUtil.logEvent("CREATE_DATA_SOURCE_CLICK", {
@ -206,11 +203,12 @@ class DatasourceHomeScreen extends React.Component<Props> {
});
}}
>
<DatasourceContentWrapper>
<DatasourceContentWrapper data-testid="database-datasource-content-wrapper">
<div className="dataSourceImageWrapper">
<img
alt="Datasource"
className="dataSourceImage"
data-testid="database-datasource-image"
src={pluginImages[plugin.id]}
/>
</div>

View File

@ -532,6 +532,7 @@ class IntegrationsHomeScreen extends React.Component<
<MainTabsContainer>
{showTabs && (
<TabComponent
cypressSelector="t--datasource-tab"
onSelect={this.onSelectPrimaryMenu}
selectedIndex={this.state.activePrimaryMenuId}
tabs={PRIMARY_MENU}

View File

@ -15,21 +15,20 @@ const MockDataSourceWrapper = styled.div`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
flex: 1;
.sectionHeader {
font-weight: ${(props) => props.theme.fontWeights[2]};
font-size: ${(props) => props.theme.fontSizes[4]}px;
margin-top: 10px;
}
min-width: 150px;
border-radius: 4px;
align-items: center;
margin-top: 8px;
`;
const Description = styled.div`
color: ${Colors.DOVE_GRAY};
font-size: 14px;
display: inline-block;
margin-top: 11px;
color: ${Colors.GREY_8};
font-size: 13px;
font-weight: 400;
line-height: 17px;
letter-spacing: -0.24px;
`;
function MockDataSources(props: { mockDatasources: MockDatasource[] }) {
const workspaceId = useSelector(getCurrentWorkspaceId);
return (
@ -50,38 +49,51 @@ function MockDataSources(props: { mockDatasources: MockDatasource[] }) {
export default MockDataSources;
const CardWrapper = styled.div`
padding: 18px;
/* margin-top: 18px; */
display: flex;
align-items: center;
justify-content: space-between;
height: 64px;
&:hover {
background: ${Colors.Gallery};
background-color: ${Colors.GREY_1};
cursor: pointer;
.bp3-collapse-body {
background: ${Colors.Gallery};
}
}
}
`;
const DatasourceImage = styled.img`
height: 24px;
height: 28px;
width: auto;
margin: 0 auto;
max-width: 100%;
`;
const DatasourceName = styled.span`
margin-left: 10px;
font-size: 16px;
font-weight: 500;
font-weight: 400;
line-height: 24px;
letter-spacing: -0.24px;
color: ${Colors.BLACK};
`;
const DatasourceCardHeader = styled.div`
justify-content: space-between;
display: flex;
align-items: center;
gap: 13px;
padding-left: 13.5px;
`;
const DatasourceIconWrapper = styled.div`
width: 48px;
height: 48px;
border-radius: 50%;
background: ${Colors.GREY_2};
display: flex;
align-items: center;
`;
const DatasourceNameWrapper = styled.div`
flex-direction: row;
align-items: center;
display: flex;
flex-direction: column;
`;
type MockDatasourceCardProps = {
@ -130,17 +142,21 @@ function MockDatasourceCard(props: MockDatasourceCardProps) {
return (
<CardWrapper className="t--mock-datasource" onClick={addMockDataSource}>
<DatasourceCardHeader className="t--datasource-name">
<div style={{ flex: 1 }}>
<DatasourceNameWrapper>
<DatasourceImage
alt="Datasource"
className="dataSourceImage"
src={pluginImages[currentPlugin.id]}
/>
<DatasourceName>{datasource.name}</DatasourceName>
</DatasourceNameWrapper>
<Description>{datasource.description}</Description>
</div>
<DatasourceIconWrapper data-testid="mock-datasource-icon-wrapper">
<DatasourceImage
alt="Datasource"
data-testid="mock-datasource-image"
src={pluginImages[currentPlugin.id]}
/>
</DatasourceIconWrapper>
<DatasourceNameWrapper data-testid="mock-datasource-name-wrapper">
<DatasourceName data-testid="mockdatasource-name">
{datasource.name}
</DatasourceName>
<Description data-testid="mockdatasource-description">
{datasource.description}
</Description>
</DatasourceNameWrapper>
</DatasourceCardHeader>
</CardWrapper>
);

View File

@ -29,9 +29,9 @@ const StyledContainer = styled.div`
margin: 0;
justify-content: center;
text-align: center;
letter-spacing: -0.17px;
color: ${Colors.OXFORD_BLUE};
font-weight: 500;
letter-spacing: -0.24px;
color: ${Colors.BLACK};
font-weight: 400;
text-decoration: none !important;
flex-wrap: wrap;
white-space: nowrap;
@ -84,17 +84,15 @@ const ApiCard = styled.div`
justify-content: space-between;
height: 64px;
&:hover {
background: ${Colors.Gallery};
background-color: ${Colors.GREY_1};
cursor: pointer;
}
.content-icon-wrapper {
width: 40px;
height: 40px;
border-radius: 20px;
padding: 6px 0;
margin: 0 8px;
background: #f0f0f0;
width: 48px;
height: 48px;
border-radius: 50%;
background: ${Colors.GREY_2};
display: flex;
align-items: center;
@ -121,6 +119,8 @@ const ApiCard = styled.div`
const CardContentWrapper = styled.div`
display: flex;
align-items: center;
gap: 13px;
padding-left: 13.5px;
`;
type ApiHomeScreenProps = {
@ -245,12 +245,12 @@ function NewApiScreen(props: Props) {
return (
<StyledContainer>
<ApiCardsContainer>
<ApiCardsContainer data-testid="newapi-datasource-card-container">
<ApiCard
className="t--createBlankApiCard create-new-api"
onClick={() => handleOnClick(API_ACTION.CREATE_NEW_API)}
>
<CardContentWrapper>
<CardContentWrapper data-testid="newapi-datasource-content-wrapper">
<div className="content-icon-wrapper">
<img
alt="New"