Skip to content

Commit

Permalink
feat: Add survey banner to Explorer (#1063)
Browse files Browse the repository at this point in the history
Co-authored-by: rainandbare <[email protected]>
  • Loading branch information
rainandbare and rainandbare authored Aug 6, 2024
1 parent cf3608f commit a875df6
Show file tree
Hide file tree
Showing 13 changed files with 314 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .infra/rdev/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ stack:
services:
explorer:
image:
tag: sha-3cf6306f
tag: sha-4c9517de
replicaCount: 1
env:
# env vars common to all deployment stages
Expand Down
42 changes: 41 additions & 1 deletion client/__tests__/e2e/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import {
pageURLSpatial,
} from "../common/constants";
import {
closeBottomBanner,
conditionallyToggleSidePanel,
goToPage,
shouldSkipTests,
Expand Down Expand Up @@ -207,6 +208,33 @@ for (const testDataset of testDatasets) {
});
});


describe("bottom banner", () => {
const SURVEY_LINK = "https://airtable.com/app8fNSQ8ieIiHLOv/shrmD31azkGtSupmO";
test("bottom banner appears", async ({ page }, testInfo) => {
await goToPage(page, url);

const bottomBanner = page.getByTestId("bottom-banner");

await expect(bottomBanner).toBeVisible();

await expect(page.getByText("quick survey")).toHaveAttribute(
"href",
SURVEY_LINK
);

await snapshotTestGraph(page, testInfo);
});
test("bottom banner disappears", async ({ page }, testInfo) => {
await goToPage(page, url);

const bottomBanner = await closeBottomBanner(page)
await expect(bottomBanner).not.toBeVisible();

await snapshotTestGraph(page, testInfo);
});
});

test("resize graph", async ({ page }, testInfo) => {
skipIfSidePanel(graphTestId, MAIN_PANEL);

Expand Down Expand Up @@ -314,6 +342,8 @@ for (const testDataset of testDatasets) {

await conditionallyToggleSidePanel(page, graphTestId, SIDE_PANEL);

await closeBottomBanner(page);

const originalCellCount = await getCellSetCount(1, page);

for (const cellset of data.cellsets.lasso) {
Expand Down Expand Up @@ -506,6 +536,8 @@ for (const testDataset of testDatasets) {

await conditionallyToggleSidePanel(page, graphTestId, SIDE_PANEL);

await closeBottomBanner(page)

const lassoSelection = await calcDragCoordinates(
graphTestId,
data.subset.lasso["coordinates-as-percent"],
Expand Down Expand Up @@ -717,6 +749,7 @@ for (const testDataset of testDatasets) {
});

describe("graph overlay", () => {

test("transform centroids correctly", async ({ page }, testInfo) => {
skipIfSidePanel(graphTestId, MAIN_PANEL);

Expand Down Expand Up @@ -815,6 +848,7 @@ for (const testDataset of testDatasets) {
}, testInfo) => {
await goToPage(page, url);
await conditionallyToggleSidePanel(page, graphTestId, SIDE_PANEL);
await closeBottomBanner(page);

await tryUntil(
async () => {
Expand Down Expand Up @@ -871,6 +905,7 @@ for (const testDataset of testDatasets) {
test("lasso moves after pan", async ({ page }, testInfo) => {
await goToPage(page, url);
await conditionallyToggleSidePanel(page, graphTestId, SIDE_PANEL);
await closeBottomBanner(page);

await tryUntil(
async () => {
Expand Down Expand Up @@ -1701,7 +1736,12 @@ async function setup({
testInfo: TestInfo;
}) {
await goToPage(page, url);

await tryUntil(
async () => {
await closeBottomBanner(page);
},
{ page }
);
if (withSubset) {
await subset({ x1: 0.1, y1: 0.15, x2: 0.8, y2: 0.85 }, page, testInfo);
}
Expand Down
12 changes: 12 additions & 0 deletions client/__tests__/util/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,15 @@ export function shouldSkipTests(
): boolean {
return graphTestId === SIDE_PANEL;
}


export async function closeBottomBanner(page: Page): Promise<Locator> {
const bottomBanner = page.getByTestId("bottom-banner");

if(bottomBanner) {
const bottomBannerClose = bottomBanner.getByRole("button");
await bottomBannerClose.click();
}

return bottomBanner;
}
7 changes: 7 additions & 0 deletions client/src/components/BottomBanner/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const BANNER_FEEDBACK_SURVEY_LINK =
"https://airtable.com/app8fNSQ8ieIiHLOv/shrmD31azkGtSupmO";
export const BOTTOM_BANNER_SURVEY_LINK_TEXT = "quick survey.";
export const BOTTOM_BANNER_SURVEY_TEXT = "Send us feedback with this";
export const BOTTOM_BANNER_LAST_CLOSED_TIME_KEY = "bottomBannerLastClosedTime";
export const BOTTOM_BANNER_EXPIRATION_TIME_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
// export const BOTTOM_BANNER_EXPIRATION_TIME_MS = 30 * 1000; // 30 seconds
70 changes: 70 additions & 0 deletions client/src/components/BottomBanner/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { memo } from "react";
import { connect } from "react-redux";
import {
BOTTOM_BANNER_ID,
StyledBanner,
StyledBottomBannerWrapper,
StyledLink,
} from "./style";
import {
BOTTOM_BANNER_SURVEY_LINK_TEXT,
BOTTOM_BANNER_SURVEY_TEXT,
} from "./constants";
import { AppDispatch, RootState } from "../../reducers";

export interface BottomBannerProps {
surveyLink: string;
showBottomBanner: boolean;
dispatch: AppDispatch;
}

const mapStateToProps = (state: RootState) => ({
showBottomBanner: state.showBottomBanner,
});

const mapDispatchToProps = (dispatch: AppDispatch) => ({
dispatch,
});

const BottomBanner = memo(
({
surveyLink,
showBottomBanner,
dispatch,
}: BottomBannerProps): JSX.Element | null => {
const setBottomBannerLastClosedTime = () => {
dispatch({
type: "update bottom banner last closed time",
time: Date.now(),
});
};

if (!showBottomBanner) return null;

return (
<>
<StyledBottomBannerWrapper
id={BOTTOM_BANNER_ID}
data-testid={BOTTOM_BANNER_ID}
>
<StyledBanner
dismissible
sdsType="primary"
onClose={setBottomBannerLastClosedTime}
// @ts-expect-error -- czifui Banner component types text prop as a string but the prop works with JSX as well
text={
<span>
{BOTTOM_BANNER_SURVEY_TEXT}
<StyledLink href={surveyLink} target="_blank" rel="noopener">
{BOTTOM_BANNER_SURVEY_LINK_TEXT}
</StyledLink>
</span>
}
/>
</StyledBottomBannerWrapper>
</>
);
}
);

export default connect(mapStateToProps, mapDispatchToProps)(BottomBanner);
63 changes: 63 additions & 0 deletions client/src/components/BottomBanner/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import styled from "@emotion/styled";
import { Banner, Icon } from "czifui";
import { beta100, beta400, gray500 } from "../theme";

export const SKINNY_MODE_BREAKPOINT_WIDTH = 960;
export const BOTTOM_BANNER_ID = "bottom-banner";

export const StyledBanner = styled(Banner)`
@media (max-width: ${SKINNY_MODE_BREAKPOINT_WIDTH}px) {
padding: 8px 16px;
box-shadow: 0px 0px 4px 0px rgba(50, 50, 50, 0.75);
}
span {
font-family: "Roboto Condensed", "Helvetica Neue", "Helvetica", "Arial",
sans-serif;
font-weight: 400;
}
/**
* beta intent does not exist for SDS banner, but the colors do targeting
* specific id to overwrite style
*/
border-color: ${beta400} !important;
background-color: ${beta100};
color: black;
/* Hide default svg icon in the Banner as it is not in figma */
:first-of-type > div:first-of-type > div:first-of-type {
display: none;
}
/* Change close button icon default color */
button svg {
path {
fill: ${gray500};
}
}
`;

export const StyledBottomBannerWrapper = styled.div`
width: 100%;
/* Right behind modal overlay */
z-index: 19;
background-color: purple;
`;

export const StyledLink = styled.a`
padding: 0px 5px 0px 5px;
text-decoration-line: underline;
color: #8f5aff;
font-weight: 500;
:hover {
color: #5826c1;
}
`;

const STYLED_CLOSE_BUTTON_ICON_DENY_PROPS = ["hideCloseButton"];

export const StyledCloseButtonIcon = styled(Icon, {
shouldForwardProp: (prop) =>
!STYLED_CLOSE_BUTTON_ICON_DENY_PROPS.includes(prop),
})``;
57 changes: 31 additions & 26 deletions client/src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { connect } from "react-redux";
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
import { StylesProvider, ThemeProvider } from "@material-ui/core/styles";
import { theme } from "./theme";
import BottomBanner from "./BottomBanner";

import Controls from "./controls";
import DatasetSelector from "./datasetSelector/datasetSelector";
Expand All @@ -23,6 +24,7 @@ import Graph from "./graph/graph";
import DiffexNotice from "./diffexNotice";
import Scatterplot from "./scatterplot/scatterplot";
import PanelEmbedding from "./PanelEmbedding";
import { BANNER_FEEDBACK_SURVEY_LINK } from "./BottomBanner/constants";

interface StateProps {
loading: RootState["controls"]["loading"];
Expand Down Expand Up @@ -99,32 +101,35 @@ class App extends React.Component<StateProps & { dispatch: AppDispatch }> {
<Header tosURL={tosURL} privacyURL={privacyURL} />
)}
{loading || error ? null : (
<Layout
addTopPadding={!datasetMetadataError || isCellGuideCxg}
renderGraph={(viewportRef: HTMLDivElement) => (
<>
<GlobalHotkeys />
<Controls>
<MenuBar />
</Controls>
<Legend />
<Graph
viewportRef={viewportRef}
key={graphRenderCounter}
/>
{scatterplotXXaccessor && scatterplotYYaccessor && (
<Scatterplot />
)}
<PanelEmbedding />
<Controls bottom={0}>
<DatasetSelector />
</Controls>
</>
)}
>
<LeftSideBar />
<RightSideBar />
</Layout>
<>
<Layout
addTopPadding={!datasetMetadataError || isCellGuideCxg}
renderGraph={(viewportRef: HTMLDivElement) => (
<>
<GlobalHotkeys />
<Controls>
<MenuBar />
</Controls>
<Legend />
<Graph
viewportRef={viewportRef}
key={graphRenderCounter}
/>
{scatterplotXXaccessor && scatterplotYYaccessor && (
<Scatterplot />
)}
<PanelEmbedding />
<Controls bottom={0}>
<DatasetSelector />
</Controls>
</>
)}
>
<LeftSideBar />
<RightSideBar />
</Layout>
<BottomBanner surveyLink={BANNER_FEEDBACK_SURVEY_LINK} />
</>
)}
</ThemeProvider>
</EmotionThemeProvider>
Expand Down
Loading

0 comments on commit a875df6

Please sign in to comment.