Skip to content
Snippets Groups Projects
Unverified Commit 73c71c06 authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Clean up QB sidebar layout components (#20617)

* Convert `ViewButton` to TypeScript

* Convert SidebarContent to TypeScript

* Convert SidebarHeader to TypeScript

* Fix E2E tests

* Remove useMemo usage
parent d9c51ec7
No related branches found
No related tags found
No related merge requests found
Showing
with 233 additions and 51 deletions
import styled from "@emotion/styled";
import ViewButton from "../view/ViewButton";
export const SidebarContentRoot = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
`;
export const SidebarContentMain = styled.div`
overflow-y: auto;
`;
export const FooterButton = styled(ViewButton)`
margin-left: auto;
margin-right: auto;
margin-top: 0.5rem;
margin-bottom: 1rem;
padding-left: 4rem;
padding-right: 4rem;
border-radius: 99px;
box-shadow: 0 2px 2px rgb(0 0 0 / 13%);
`;
FooterButton.defaultProps = {
active: true,
};
/* eslint-disable react/prop-types */
import React from "react";
import ViewButton from "./view/ViewButton";
import SidebarHeader from "./SidebarHeader";
import cx from "classnames";
import React, { ReactNode } from "react";
import { t } from "ttag";
import SidebarHeader from "../SidebarHeader";
import {
SidebarContentRoot,
SidebarContentMain,
FooterButton,
} from "./SidebarContent.styled";
type Props = {
className?: string;
title?: string;
icon?: string;
color?: string;
onBack?: () => void;
onClose?: () => void;
onDone?: () => void;
doneButtonText?: string;
footer?: ReactNode;
children?: ReactNode;
};
export default function SidebarContent({
function SidebarContent({
className,
icon,
title,
icon,
color,
onBack,
onClose,
......@@ -22,13 +35,11 @@ export default function SidebarContent({
</FooterButton>
) : null,
children,
}) {
}: Props) {
return (
<div
className={cx(className, "flex flex-column justify-between full-height")}
>
<div className="scroll-y">
{(title || onBack || icon) && (
<SidebarContentRoot className={className}>
<SidebarContentMain data-testid="sidebar-content">
{(title || icon || onBack) && (
<SidebarHeader
className="mx3 my2 pt1"
title={title}
......@@ -38,21 +49,10 @@ export default function SidebarContent({
/>
)}
{children}
</div>
</SidebarContentMain>
{footer}
</div>
</SidebarContentRoot>
);
}
const FooterButton = props => (
<ViewButton
active
px={4}
ml="auto"
mr="auto"
mb={2}
mt={1}
className="circular shadowed"
{...props}
/>
);
export default SidebarContent;
export { default } from "./SidebarContent";
/* eslint-disable react/prop-types */
import React from "react";
import cx from "classnames";
import { t } from "ttag";
import Icon from "metabase/components/Icon";
export default function SidebarHeader({
className,
title,
icon,
onBack,
onClose,
}) {
const backDefault = !title && !!onBack;
if (backDefault) {
title = t`Back`;
}
return (
<div className={cx("flex align-center", className)}>
<span
className={cx("flex align-center text-heavy h3", {
"cursor-pointer text-brand-hover": onBack,
"h5 text-medium text-uppercase": backDefault,
})}
onClick={onBack}
>
{(onBack || icon) && (
<Icon name={icon || "chevronleft"} className="mr1" />
)}
{title}
</span>
{onClose && (
<a
className="flex-align-right text-medium text-brand-hover no-decoration"
onClick={onClose}
>
<Icon name="close" size={18} />
</a>
)}
</div>
);
}
import styled from "@emotion/styled";
import { css } from "@emotion/react";
import Icon from "metabase/components/Icon";
import { color } from "metabase/lib/colors";
export const HeaderRoot = styled.div`
display: flex;
align-items: center;
`;
export const HeaderIcon = styled(Icon)`
margin-right: 0.5rem;
`;
const backButtonStyle = css`
cursor: pointer;
&:hover {
color: ${color("brand")};
}
`;
const defaultBackButtonStyle = css`
${backButtonStyle}
color: ${color("text-medium")};
font-size: 0.83em;
text-transform: uppercase;
letter-spacing: 0.06em;
`;
export type HeaderTitleContainerVariant =
| "default"
| "back-button"
| "default-back-button";
function getHeaderTitleContainerVariantStyle(
variant: HeaderTitleContainerVariant = "default",
) {
if (variant === "default") {
return;
}
return variant === "default-back-button"
? defaultBackButtonStyle
: backButtonStyle;
}
export const HeaderTitleContainer = styled.span<{
variant?: HeaderTitleContainerVariant;
}>`
display: flex;
align-items: center;
font-size: 1.17em;
font-weight: 900;
margin-top: 0;
margin-bottom: 0;
${props => getHeaderTitleContainerVariantStyle(props.variant)}
`;
export const CloseButton = styled.a`
color: ${color("text-medium")};
text-decoration: none;
margin-left: auto;
&:hover {
color: ${color("brand")};
}
`;
import React from "react";
import { t } from "ttag";
import Icon from "metabase/components/Icon";
import {
HeaderRoot,
HeaderIcon,
HeaderTitleContainer,
HeaderTitleContainerVariant,
CloseButton,
} from "./SidebarHeader.styled";
type Props = {
className?: string;
title?: string;
icon?: string;
onBack?: () => void;
onClose?: () => void;
};
function getHeaderVariant({
hasDefaultBackButton,
hasOnBackHandler,
}: {
hasDefaultBackButton: boolean;
hasOnBackHandler: boolean;
}): HeaderTitleContainerVariant {
if (hasDefaultBackButton) {
return "default-back-button";
}
if (hasOnBackHandler) {
return "back-button";
}
return "default";
}
function SidebarHeader({ className, title, icon, onBack, onClose }: Props) {
const hasDefaultBackButton = !title && !!onBack;
const headerVariant = getHeaderVariant({
hasDefaultBackButton,
hasOnBackHandler: !!onBack,
});
const hasHeaderIcon = onBack || icon;
return (
<HeaderRoot className={className}>
<HeaderTitleContainer variant={headerVariant} onClick={onBack}>
{hasHeaderIcon && <HeaderIcon name={icon || "chevronleft"} />}
{hasDefaultBackButton ? t`Back` : title}
</HeaderTitleContainer>
{onClose && (
<CloseButton onClick={onClose}>
<Icon name="close" size={18} />
</CloseButton>
)}
</HeaderRoot>
);
}
export default SidebarHeader;
export { default } from "./SidebarHeader";
import styled from "@emotion/styled";
import Button from "metabase/core/components/Button";
import { color, alpha } from "metabase/lib/colors";
import styled from "@emotion/styled";
type Props = {
active?: boolean;
color?: string;
};
// NOTE: some of this is duplicated from NotebookCell.jsx
const ViewButton = styled(Button)`
const ViewButton = styled(Button)<Props>`
background-color: ${({ active, color = getDefaultColor() }) =>
active ? color : alpha(color, 0.2)};
color: ${({ active, color = getDefaultColor() }) =>
active ? "white" : color};
border: none;
transition: background 300ms linear, border 300ms linear;
&:hover {
background-color: ${({ active, color = getDefaultColor() }) =>
active ? alpha(color, 0.8) : alpha(color, 0.35)};
color: ${({ active, color = getDefaultColor() }) =>
active ? "white" : color};
}
transition: background 300ms linear, border 300ms linear;
> .Icon {
opacity: 0.6;
}
......
......@@ -123,7 +123,7 @@ describe("scenarios > question > filter", () => {
cy.get(".List-item-title")
.contains("Product ID")
.click();
cy.get(".scroll-y")
cy.findByTestId("sidebar-content")
.contains("Aerodynamic Linen Coat")
.click();
cy.findByText("Add filter").click();
......
......@@ -31,10 +31,7 @@ describe("scenarios > question > settings", () => {
.invoke("width")
.should("be.gt", 350);
cy.contains("Table options")
.parents(".scroll-y")
.first()
.as("tableOptions");
cy.findByTestId("sidebar-content").as("tableOptions");
// remove Total column
cy.get("@tableOptions")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment