Skip to content

Commit a44fd2c

Browse files
amanaperbren
authored andcommitted
hotfix(frontend): Consistent buttons and their styles throughout the app (All-Hands-AI#6835)
Co-authored-by: Robert Brennan <[email protected]>
1 parent 1a9b284 commit a44fd2c

35 files changed

+137
-123
lines changed

frontend/__tests__/components/modals/settings/brand-button.test.tsx frontend/__tests__/components/shared/brand-button.test.tsx

+16
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,20 @@ describe("BrandButton", () => {
3636

3737
expect(screen.getByText("Test Button")).toBeDisabled();
3838
});
39+
40+
it("should pass a start content", () => {
41+
render(
42+
<BrandButton
43+
type="button"
44+
variant="primary"
45+
startContent={
46+
<div data-testid="custom-start-content">Start Content</div>
47+
}
48+
>
49+
Test Button
50+
</BrandButton>,
51+
);
52+
53+
screen.getByTestId("custom-start-content");
54+
});
3955
});
Loading

frontend/src/components/features/analytics/analytics-consent-form-modal.tsx

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { ModalButton } from "#/components/shared/buttons/modal-button";
21
import {
32
BaseModalTitle,
43
BaseModalDescription,
@@ -7,6 +6,7 @@ import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
76
import { ModalBody } from "#/components/shared/modals/modal-body";
87
import { useCurrentSettings } from "#/context/settings-context";
98
import { handleCaptureConsent } from "#/utils/handle-capture-consent";
9+
import { BrandButton } from "../settings/brand-button";
1010

1111
interface AnalyticsConsentFormModalProps {
1212
onClose: () => void;
@@ -40,7 +40,7 @@ export function AnalyticsConsentFormModal({
4040
onSubmit={handleSubmit}
4141
className="flex flex-col gap-2"
4242
>
43-
<ModalBody>
43+
<ModalBody className="border border-tertiary">
4444
<BaseModalTitle title="Your Privacy Preferences" />
4545
<BaseModalDescription>
4646
We use tools to understand how our application is used to improve
@@ -53,12 +53,14 @@ export function AnalyticsConsentFormModal({
5353
Send anonymous usage data
5454
</label>
5555

56-
<ModalButton
56+
<BrandButton
5757
testId="confirm-preferences"
5858
type="submit"
59-
text="Confirm Preferences"
60-
className="bg-primary text-white w-full hover:opacity-80"
61-
/>
59+
variant="primary"
60+
className="w-full"
61+
>
62+
Confirm Preferences
63+
</BrandButton>
6264
</ModalBody>
6365
</form>
6466
</ModalBackdrop>

frontend/src/components/features/chat/chat-message.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function ChatMessage({
4747
className={cn(
4848
"rounded-xl relative",
4949
"flex flex-col gap-2",
50-
type === "user" && " max-w-[305px] p-4 bg-neutral-700 self-end",
50+
type === "user" && " max-w-[305px] p-4 bg-tertiary self-end",
5151
type === "assistant" && "mt-6 max-w-full bg-tranparent",
5252
)}
5353
>

frontend/src/components/features/chat/chat-suggestions.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function ChatSuggestions({ onSuggestionsClick }: ChatSuggestionsProps) {
1313

1414
return (
1515
<div className="flex flex-col gap-6 h-full px-4 items-center justify-center">
16-
<div className="flex flex-col items-center p-4 bg-neutral-700 rounded-xl w-full">
16+
<div className="flex flex-col items-center p-4 bg-tertiary rounded-xl w-full">
1717
<BuildIt width={45} height={54} />
1818
<span className="font-semibold text-[20px] leading-6 -tracking-[0.01em] gap-1">
1919
{t(I18nKey.LANDING$TITLE)}

frontend/src/components/features/chat/interactive-chat-box.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function InteractiveChatBox({
5959
<div
6060
className={cn(
6161
"flex items-end gap-1",
62-
"bg-neutral-700 border border-neutral-600 rounded-lg px-2",
62+
"bg-tertiary border border-neutral-600 rounded-lg px-2",
6363
"transition-colors duration-200",
6464
"hover:border-neutral-500 focus-within:border-neutral-500",
6565
)}

frontend/src/components/features/chat/typing-indicator.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export function TypingIndicator() {
22
return (
3-
<div className="flex items-center space-x-1.5 bg-neutral-700 px-3 py-1.5 rounded-full">
3+
<div className="flex items-center space-x-1.5 bg-tertiary px-3 py-1.5 rounded-full">
44
<span
55
className="w-1.5 h-1.5 bg-gray-400 rounded-full animate-[bounce_0.5s_infinite] translate-y-[-2px]"
66
style={{ animationDelay: "0ms" }}

frontend/src/components/features/context-menu/context-menu.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function ContextMenu({
1818
<ul
1919
data-testid={testId}
2020
ref={ref}
21-
className={cn("bg-[#404040] rounded-md w-[140px]", className)}
21+
className={cn("bg-tertiary rounded-md w-[140px]", className)}
2222
>
2323
{children}
2424
</ul>

frontend/src/components/features/conversation-panel/confirm-delete-modal.tsx

+16-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { ModalButton } from "#/components/shared/buttons/modal-button";
21
import {
32
BaseModalDescription,
43
BaseModalTitle,
54
} from "#/components/shared/modals/confirmation-modals/base-modal";
65
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
76
import { ModalBody } from "#/components/shared/modals/modal-body";
7+
import { BrandButton } from "../settings/brand-button";
88

99
interface ConfirmDeleteModalProps {
1010
onConfirm: () => void;
@@ -17,7 +17,7 @@ export function ConfirmDeleteModal({
1717
}: ConfirmDeleteModalProps) {
1818
return (
1919
<ModalBackdrop>
20-
<ModalBody className="items-start">
20+
<ModalBody className="items-start border border-tertiary">
2121
<div className="flex flex-col gap-2">
2222
<BaseModalTitle title="Are you sure you want to delete this project?" />
2323
<BaseModalDescription description="All data associated with this project will be lost." />
@@ -26,16 +26,22 @@ export function ConfirmDeleteModal({
2626
className="flex flex-col gap-2 w-full"
2727
onClick={(event) => event.stopPropagation()}
2828
>
29-
<ModalButton
29+
<BrandButton
30+
type="button"
31+
variant="primary"
3032
onClick={onConfirm}
31-
className="bg-danger font-bold"
32-
text="Confirm"
33-
/>
34-
<ModalButton
33+
className="w-full"
34+
>
35+
Confirm
36+
</BrandButton>
37+
<BrandButton
38+
type="button"
39+
variant="secondary"
3540
onClick={onCancel}
36-
className="bg-neutral-500 font-bold"
37-
text="Cancel"
38-
/>
41+
className="w-full"
42+
>
43+
Cancel
44+
</BrandButton>
3945
</div>
4046
</ModalBody>
4147
</ModalBackdrop>

frontend/src/components/features/conversation-panel/exit-conversation-modal.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function ExitConversationModal({
2525
<ModalButton
2626
text="Cancel"
2727
onClick={onClose}
28-
className="bg-neutral-700 flex-1"
28+
className="bg-tertiary flex-1"
2929
/>
3030
</div>
3131
</ModalBody>

frontend/src/components/features/feedback/feedback-form.tsx

+16-11
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
44
import { I18nKey } from "#/i18n/declaration";
55
import { Feedback } from "#/api/open-hands.types";
66
import { useSubmitFeedback } from "#/hooks/mutation/use-submit-feedback";
7-
import { ModalButton } from "#/components/shared/buttons/modal-button";
7+
import { BrandButton } from "../settings/brand-button";
88

99
const FEEDBACK_VERSION = "1.0";
1010
const VIEWER_PAGE = "https://www.all-hands.dev/share";
@@ -121,18 +121,23 @@ export function FeedbackForm({ onClose, polarity }: FeedbackFormProps) {
121121
</div>
122122

123123
<div className="flex gap-2">
124-
<ModalButton
125-
disabled={isPending}
124+
<BrandButton
126125
type="submit"
127-
text={t(I18nKey.FEEDBACK$SHARE_LABEL)}
128-
className="bg-[#4465DB] grow"
129-
/>
130-
<ModalButton
131-
disabled={isPending}
132-
text={t(I18nKey.FEEDBACK$CANCEL_LABEL)}
126+
variant="primary"
127+
className="grow"
128+
isDisabled={isPending}
129+
>
130+
{t(I18nKey.FEEDBACK$SHARE_LABEL)}
131+
</BrandButton>
132+
<BrandButton
133+
type="button"
134+
variant="secondary"
135+
className="grow"
133136
onClick={onClose}
134-
className="bg-[#737373] grow"
135-
/>
137+
isDisabled={isPending}
138+
>
139+
{t(I18nKey.FEEDBACK$CANCEL_LABEL)}
140+
</BrandButton>
136141
</div>
137142
</form>
138143
);

frontend/src/components/features/feedback/feedback-modal.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export function FeedbackModal({
2121

2222
return (
2323
<ModalBackdrop onClose={onClose}>
24-
<ModalBody>
24+
<ModalBody className="border border-tertiary">
2525
<BaseModalTitle title="Feedback" />
2626
<BaseModalDescription description="To help us improve, we collect feedback from your interactions to improve our prompts. By submitting this form, you consent to us collecting this data." />
2727
<FeedbackForm onClose={onClose} polarity={polarity} />

frontend/src/components/features/file-explorer/file-explorer.tsx

+12-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import { useListFiles } from "#/hooks/query/use-list-files";
1010
import { cn } from "#/utils/utils";
1111
import { FileExplorerHeader } from "./file-explorer-header";
1212
import { useVSCodeUrl } from "#/hooks/query/use-vscode-url";
13-
import { OpenVSCodeButton } from "#/components/shared/buttons/open-vscode-button";
13+
import { BrandButton } from "../settings/brand-button";
14+
import VSCodeIcon from "#/assets/vscode-alt.svg?react";
1415

1516
interface FileExplorerProps {
1617
isOpen: boolean;
@@ -77,10 +78,17 @@ export function FileExplorer({ isOpen, onToggle }: FileExplorerProps) {
7778
</div>
7879
)}
7980
{isOpen && (
80-
<OpenVSCodeButton
81-
onClick={handleOpenVSCode}
81+
<BrandButton
82+
testId="open-vscode-button"
83+
type="button"
84+
variant="secondary"
85+
className="w-full text-content border-content"
8286
isDisabled={RUNTIME_INACTIVE_STATES.includes(curAgentState)}
83-
/>
87+
onClick={handleOpenVSCode}
88+
startContent={<VSCodeIcon width={20} height={20} />}
89+
>
90+
{t(I18nKey.VSCODE$OPEN)}
91+
</BrandButton>
8492
)}
8593
</div>
8694
</div>

frontend/src/components/features/github/github-repositories-suggestion-box.tsx

+10-7
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import { useTranslation } from "react-i18next";
33
import { useNavigate } from "react-router";
44
import { I18nKey } from "#/i18n/declaration";
55
import { SuggestionBox } from "#/components/features/suggestions/suggestion-box";
6-
import GitHubLogo from "#/assets/branding/github-logo.svg?react";
76
import { GitHubRepositorySelector } from "./github-repo-selector";
8-
import { ModalButton } from "#/components/shared/buttons/modal-button";
97
import { useAppRepositories } from "#/hooks/query/use-app-repositories";
108
import { useSearchRepositories } from "#/hooks/query/use-search-repositories";
119
import { useUserRepositories } from "#/hooks/query/use-user-repositories";
1210
import { sanitizeQuery } from "#/utils/sanitize-query";
1311
import { useDebounce } from "#/hooks/use-debounce";
12+
import { BrandButton } from "../settings/brand-button";
13+
import GitHubLogo from "#/assets/branding/github-logo.svg?react";
1414

1515
interface GitHubRepositoriesSuggestionBoxProps {
1616
handleSubmit: () => void;
@@ -62,13 +62,16 @@ export function GitHubRepositoriesSuggestionBox({
6262
userRepositories={repositories}
6363
/>
6464
) : (
65-
<ModalButton
65+
<BrandButton
6666
testId="connect-to-github"
67-
text={t(I18nKey.GITHUB$CONNECT)}
68-
icon={<GitHubLogo width={20} height={20} />}
69-
className="bg-[#791B80] w-full"
67+
type="button"
68+
variant="secondary"
69+
className="w-full text-content border-content"
7070
onClick={handleConnectToGitHub}
71-
/>
71+
startContent={<GitHubLogo width={20} height={20} />}
72+
>
73+
{t(I18nKey.GITHUB$CONNECT)}
74+
</BrandButton>
7275
)
7376
}
7477
/>

frontend/src/components/features/settings/brand-button.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ interface BrandButtonProps {
77
isDisabled?: boolean;
88
className?: string;
99
onClick?: () => void;
10+
startContent?: React.ReactNode;
1011
}
1112

1213
export function BrandButton({
@@ -17,6 +18,7 @@ export function BrandButton({
1718
isDisabled,
1819
className,
1920
onClick,
21+
startContent,
2022
}: React.PropsWithChildren<BrandButtonProps>) {
2123
return (
2224
<button
@@ -30,9 +32,11 @@ export function BrandButton({
3032
"w-fit p-2 rounded disabled:opacity-30 disabled:cursor-not-allowed",
3133
variant === "primary" && "bg-primary text-[#0D0F11]",
3234
variant === "secondary" && "border border-primary text-primary",
35+
startContent && "flex items-center justify-center gap-2",
3336
className,
3437
)}
3538
>
39+
{startContent}
3640
{children}
3741
</button>
3842
);

frontend/src/components/features/suggestions/suggestion-item.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ interface SuggestionItemProps {
1111
export function SuggestionItem({ suggestion, onClick }: SuggestionItemProps) {
1212
const { t } = useTranslation();
1313
return (
14-
<li className="list-none border border-neutral-600 rounded-xl hover:bg-neutral-700 flex-1">
14+
<li className="list-none border border-neutral-600 rounded-xl hover:bg-tertiary flex-1">
1515
<button
1616
type="button"
1717
data-testid="suggestion"

frontend/src/components/features/waitlist/waitlist-modal.tsx

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from "react";
2-
import GitHubLogo from "#/assets/branding/github-logo.svg?react";
32
import AllHandsLogo from "#/assets/branding/all-hands-logo.svg?react";
43
import { JoinWaitlistAnchor } from "./join-waitlist-anchor";
54
import { WaitlistMessage } from "./waitlist-message";
65
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
7-
import { ModalButton } from "#/components/shared/buttons/modal-button";
86
import { ModalBody } from "#/components/shared/modals/modal-body";
97
import { TOSCheckbox } from "./tos-checkbox";
108
import { handleCaptureConsent } from "#/utils/handle-capture-consent";
9+
import { BrandButton } from "../settings/brand-button";
10+
import GitHubLogo from "#/assets/branding/github-logo.svg?react";
1111

1212
interface WaitlistModalProps {
1313
ghTokenIsSet: boolean;
@@ -29,20 +29,23 @@ export function WaitlistModal({
2929

3030
return (
3131
<ModalBackdrop>
32-
<ModalBody>
32+
<ModalBody className="border border-tertiary">
3333
<AllHandsLogo width={68} height={46} />
3434
<WaitlistMessage content={ghTokenIsSet ? "waitlist" : "sign-in"} />
3535

3636
<TOSCheckbox onChange={() => setIsTosAccepted((prev) => !prev)} />
3737

3838
{!ghTokenIsSet && (
39-
<ModalButton
40-
disabled={!isTosAccepted}
41-
text="Connect to GitHub"
42-
icon={<GitHubLogo width={20} height={20} />}
43-
className="bg-[#791B80] w-full"
39+
<BrandButton
40+
isDisabled={!isTosAccepted}
41+
type="button"
42+
variant="primary"
4443
onClick={handleGitHubAuth}
45-
/>
44+
className="w-full"
45+
startContent={<GitHubLogo width={20} height={20} />}
46+
>
47+
Connect to GitHub
48+
</BrandButton>
4649
)}
4750
{ghTokenIsSet && <JoinWaitlistAnchor />}
4851
</ModalBody>

0 commit comments

Comments
 (0)