Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/custom button #625

Open
wants to merge 26 commits into
base: feature/ant5
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ee6d0d7
bump antd and typescript, and update build tools and components to wo…
interim17 Nov 4, 2024
334431a
consolidate ts.config
interim17 Nov 4, 2024
1c629d5
Merge branch 'feature/ant5' of https://github.com/simularium/simulari…
interim17 Nov 18, 2024
ad19491
replace ant-vars.less with ConfigProvider
interim17 Nov 20, 2024
2a6b563
semantic color variables names for config provider
interim17 Nov 20, 2024
f24d313
align css var colors and theme color variables, divide light and dark…
interim17 Nov 21, 2024
58c7ea1
consolidate theme variables
interim17 Nov 27, 2024
24fd57e
styling fixes for antd5
interim17 Dec 3, 2024
cd63f17
fix record icon position
interim17 Dec 4, 2024
4cb5dec
merge dropdown button changes
interim17 Jan 13, 2025
eabc972
pass onClick into dropdown menu items
interim17 Jan 13, 2025
d2f0662
fix meta data panel font sizes
interim17 Jan 13, 2025
68cbed4
fix colorpicker alignment offset
interim17 Jan 13, 2025
e7b159c
remove unused commented style rule
interim17 Jan 13, 2025
857baef
remove unused themeConfig
interim17 Jan 13, 2025
c3e87d7
apply styling fixes and use new CustomButton component to streamline …
interim17 Jan 15, 2025
eaf84b3
bump viewer version
interim17 Jan 15, 2025
603b4fa
replace clickHandler with onClick
interim17 Jan 15, 2025
c9c58b1
a few more ant migration style fixes
interim17 Jan 15, 2025
69cd8ef
Merge branch 'feature/ant5' of https://github.com/simularium/simulari…
interim17 Jan 15, 2025
7000cd9
restore className on dropdown render div
interim17 Jan 15, 2025
625f288
Merge branch 'feature/ant5-styling' of https://github.com/simularium/…
interim17 Jan 15, 2025
ca5eba0
remove duplicate import
interim17 Jan 15, 2025
54580dd
remove unused css vars
interim17 Jan 15, 2025
ace0823
remove unused import
interim17 Jan 15, 2025
d504830
use plain const for tooltip text on custom buttons
interim17 Jan 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
464 changes: 7 additions & 457 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions src/components/AgentTree/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@
height: 40px;
}

.container :global(.header-checkbox .ant-checkbox) {
top: 3.5px;
}

.sub-menu {
flex-flow: row;
}
Expand Down
3 changes: 3 additions & 0 deletions src/components/ColorPicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useDebounce } from "use-debounce";

import { AGENT_COLORS } from "../../containers/ViewerPanel/constants";
import { ColorChange } from "../../constants/interfaces";
import { COLORPICKER_POPOVER_OFFSET } from "../../constants";
import { SetRecentColorsAction } from "../../state/selection/types";

import styles from "./style.css";
Expand Down Expand Up @@ -164,6 +165,8 @@ const ColorPicker = ({
placement="bottomLeft"
onOpenChange={togglePopover}
trigger="click"
overlayInnerStyle={{ padding: 0 }}
align={{ targetOffset: COLORPICKER_POPOVER_OFFSET }}
>
<label
className={classNames(
Expand Down
19 changes: 10 additions & 9 deletions src/components/ConversionCancelModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Button } from "antd";
import { CustomButton } from "../CustomButton";
import React from "react";
import classNames from "classnames";

import { ButtonClass } from "../../constants/interfaces";
import CustomModal from "../CustomModal";

import styles from "./style.css";

interface ConversionCancelModalProps {
continueProcessing: () => void;
cancelProcessing: () => void;
Expand All @@ -17,15 +15,18 @@ const ConversionCancelModal: React.FC<ConversionCancelModalProps> = ({
}) => {
const footerButtons = (
<>
<Button
className={classNames("primary-button", styles.wideButton)}
<CustomButton
variant={ButtonClass.LightPrimary}
onClick={cancelProcessing}
>
Yes, cancel
</Button>
<Button className="secondary-button" onClick={continueProcessing}>
</CustomButton>
<CustomButton
variant={ButtonClass.LightSecondary}
onClick={continueProcessing}
>
No
</Button>
</CustomButton>
</>
);

Expand Down
3 changes: 0 additions & 3 deletions src/components/ConversionCancelModal/style.css

This file was deleted.

7 changes: 4 additions & 3 deletions src/components/ConversionErrorModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Button } from "antd";
import React from "react";
import classNames from "classnames";

import { ButtonClass } from "../../constants/interfaces";
import { CustomButton } from "../CustomButton";
import CustomModal from "../CustomModal";
import { Exclamation } from "../Icons";

Expand All @@ -19,9 +20,9 @@ const ConversionErrorModal: React.FC<ConversionErrorModalProps> = ({
showForumMessage = true,
}) => {
const footerButton = (
<Button className="primary-button" onClick={closeModal}>
<CustomButton variant={ButtonClass.LightPrimary} onClick={closeModal}>
OK
</Button>
</CustomButton>
);

return (
Expand Down
20 changes: 12 additions & 8 deletions src/components/ConversionProcessingOverlay/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { useState } from "react";
import { Button, Divider, Spin } from "antd";
import { Divider, Spin } from "antd";

import { ButtonClass } from "../../constants/interfaces";
import ConversionCancelModal from "../ConversionCancelModal";
import { CustomButton } from "../CustomButton";
import { UpRightArrow, GoBack } from "../Icons";

import styles from "./style.css";
Expand Down Expand Up @@ -48,24 +50,26 @@ const ConversionProcessingOverlay = ({
</div>
<div>Processing...</div>
</div>
<h3>Open another instance of Simularium </h3>
<Button
className="secondary-button"
<h3 className={styles.newInstanceText}>
Open another instance of Simularium
</h3>
<CustomButton
variant={ButtonClass.LightSecondary}
href={`https://simularium.allencell.org/viewer`}
target="_blank"
>
Open in new tab {UpRightArrow}
</Button>
</CustomButton>
</div>
<div className={styles.dividerContainer}>
<Divider> </Divider>
</div>
<Button
className="secondary-button"
<CustomButton
variant={ButtonClass.LightSecondary}
onClick={() => setCancelModalOpen(true)}
>
Cancel file import
</Button>
</CustomButton>
</div>
);

Expand Down
7 changes: 7 additions & 0 deletions src/components/ConversionProcessingOverlay/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

.title {
color: var(--light-theme-color);
font-size: 24px;
font-weight: 600;
}

Expand All @@ -38,6 +39,12 @@
padding: 30px 50px 10px;
}

.new-instance-text {
font-size: 16px;
font-weight: 500;
margin-bottom: 0.5em;
}

.divider-container {
width: 496px;
margin-bottom: 26px;
Expand Down
205 changes: 205 additions & 0 deletions src/components/CustomButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import React, { ReactNode, useState, useEffect, forwardRef } from "react";

Check warning on line 1 in src/components/CustomButton/index.tsx

View workflow job for this annotation

GitHub Actions / Lint

'useEffect' is defined but never used
import { Button as AntButton, Tooltip } from "antd";
import styled, { css, RuleSet } from "styled-components";
import type { ButtonProps as AntButtonProps } from "antd";
import {
NAV_BAR_TOOLTIP_OFFSET,
TOOLTIP_COLOR,
TOOLTIP_DELAY,
} from "../../constants";
import { ButtonClass, TooltipPlacement } from "../../constants/interfaces";

interface CustomButtonProps extends Omit<AntButtonProps, "type" | "variant"> {
variant?: ButtonClass;
titleText?: string;
icon?: ReactNode;
onClick?: () => void;
}

interface TooltipText {
defaultText: string;
disabledText?: string;
}

interface TooltipButtonProps extends CustomButtonProps {
tooltipText: TooltipText;
tooltipPlacement?: TooltipPlacement;
}

const baseStyles = css`
font-family: ${(props) => props.theme.typography};
border-radius: 3px;
height: 32px;
padding: 6px 16px;
font-size: 14px;
align-items: center;
gap: 8px;
cursor: pointer;

&:disabled {
cursor: not-allowed;
}

&:focus-visible {
outline: 1px solid ${({ theme }) => theme.colors.lightPurpleBg};
outline-offset: 1px;
}
`;

const generateButtonStyles = (
variant: "primary" | "secondary",
theme: "light" | "dark"
) => css`
${({
theme: {
colors: { button },
},
}) => {
const buttonTheme = button[variant][theme];
const { background, text, border, hover, active, disabled } =
buttonTheme;

return css`
background-color: ${background};
border: 1px solid ${variant === "primary" ? background : border};
color: ${text};

&&& {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeesh. Which brand of CSS magic does this triple ampersand belong to? Does this mean "parent-parent-parent"..?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No! It's both better and worse than that.

In styled-components this just directly raises the specificity of the current selector. Yes, this is arbitrary and smells a bit like using !important but for me its a needed workaround for quality of life when styling antd.

Because some antd components apply many different class names, change element nesting, and even changes which base html element is rendered for different components, it can be tricky to ensure that our styles will be consistently applied. This solves that in a brute force kind of way, but is better than using !important because these triple applied class hashes CAN be overridden by something of greater selector specificity, whereas !important ignores the specificity hierarchy entirely.

When our styles don't really map onto ant's opinions very well, I prefer to just sort of veto all their styling by using this triple ampersand.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I sort of it get it now.

Does there come a point where we eschew the ant buttons altogether and just roll our own from scratch 🤔 ? I'd be curious as to when the team would consider that to be worth it.
This isn't a request for changes though! Feel free to resolve.

&:hover:not(:disabled) {
background-color: ${hover.background};
border-color: ${hover.background};
color: ${hover.text};
}

&:active:not(:disabled) {
background-color: ${hover.background};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I feel like I'd expect this to be active.background, though I can see why it would be redundant if none of the buttons change background between active and hover states.

border-color: ${active.background};
color: ${active.text};
}

&:disabled {
background-color: ${disabled.background};
border-color: ${disabled.background};
color: ${disabled.text};
}
}
`;
}}
`;

const actionStyles = css`
${({
theme: {
colors: {
button: { action },
},
},
}) => css`
background: ${action.background};
border: none;
color: ${action.text};
padding: 6px 8px;
min-width: auto;

&&& {
&:hover:not(:disabled) {
color: ${action.hover.background};
}

&:active:not(:disabled) {
border: ${action.active.text};
color: ${action.active.text};
}

&:disabled {
color: ${action.disabled.text};
}
}
`}
`;

const variantStyles: Record<ButtonClass, RuleSet<object>> = {
[ButtonClass.LightPrimary]: generateButtonStyles("primary", "light"),
[ButtonClass.LightSecondary]: generateButtonStyles("secondary", "light"),
[ButtonClass.DarkPrimary]: generateButtonStyles("primary", "dark"),
[ButtonClass.DarkSecondary]: generateButtonStyles("secondary", "dark"),
[ButtonClass.Action]: actionStyles,
};

const StyledButton = styled(AntButton)<CustomButtonProps>`
${baseStyles}
${({ variant = ButtonClass.LightPrimary }) => variantStyles[variant]}
`;

export const CustomButton = forwardRef<HTMLButtonElement, CustomButtonProps>(
(
{
children,
variant = ButtonClass.LightPrimary,
titleText,
icon,
onClick,
disabled,
...props
},
ref
) => {
return (
<StyledButton
ref={ref}
variant={variant}
onClick={onClick}
disabled={disabled}
{...props}
>
{titleText || children} {icon}
</StyledButton>
);
}
);

CustomButton.displayName = "CustomButton";

export const TooltipButton: React.FC<TooltipButtonProps> = ({
tooltipText = { defaultText: "", disabledText: "" },
tooltipPlacement,
disabled = false,
...buttonProps
}) => {
const tooltipRenderText = disabled
? tooltipText.disabledText
: tooltipText.defaultText;
const [tooltipVisible, setTooltipVisible] = useState(false);

const handleMouseEnter = (e: React.MouseEvent<HTMLElement>) => {
setTooltipVisible(true);
buttonProps.onMouseEnter?.(e);
};

const handleMouseLeave = (e: React.MouseEvent<HTMLElement>) => {
setTooltipVisible(false);
buttonProps.onMouseLeave?.(e);
};

return (
<div
className="inline-block"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<Tooltip
placement={tooltipPlacement}
title={tooltipRenderText}
color={TOOLTIP_COLOR}
mouseEnterDelay={TOOLTIP_DELAY}
align={{ targetOffset: NAV_BAR_TOOLTIP_OFFSET }}
trigger={["hover", "focus"]}
open={tooltipVisible}
>
<div>
<CustomButton {...buttonProps} disabled={disabled} />
</div>
</Tooltip>
</div>
);
};
2 changes: 2 additions & 0 deletions src/components/CustomDropdown/DropdownMenuItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,12 @@ export const DropdownRouterLink: React.FC<RouterProps> = ({
className,
to,
newTab,
onClick,
}) => (
<StyledRouterLink
to={to}
className={className}
onClick={onClick}
{...getNewTabAttributes(newTab)}
>
{children}
Expand Down
Loading
Loading