Skip to content

Commit

Permalink
Update to snackbar
Browse files Browse the repository at this point in the history
  • Loading branch information
acouch committed Feb 27, 2025
1 parent 72d9b06 commit ac34e4a
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 30 deletions.
7 changes: 3 additions & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
"updateContentCommand": "cd frontend && npm install && npm run build",
"postCreateCommand": "",
"postAttachCommand": {
"server": "cd frontend && npm start"
"server": "cd frontend && npm run storybook"
},

"portsAttributes": {
"3000": {
"6006": {
"label": "Application",
"onAutoForward": "openPreview"
}
},
"forwardPorts": [3000]
"forwardPorts": [6006]
}
25 changes: 25 additions & 0 deletions frontend/src/components/Snackbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import clsx from "clsx";

type SnackbarProps = {
isVisible: boolean;
children?: React.ReactNode;
};

const Snackbar = ({ isVisible = false, children }: SnackbarProps) => {
return (
<div
data-testid="snackbar"
className={clsx(
"text-left position-fixed padding-2 text-white font-sans-2xs radius-md bg-base-darkest usa-modal-wrapper top-0 right-0",
{
"is-visible": isVisible,
"is-hidden": !isVisible,
},
)}
>
{children}
</div>
);
};

export default Snackbar;
74 changes: 50 additions & 24 deletions frontend/src/components/search/SearchSavedQuery.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,68 @@
"use client";

import { environment } from "src/constants/environments";
import { useCopyToClipboard } from "src/hooks/useCopyToClipboard";
import { useSnackbar } from "src/hooks/useSnackbar";

import { usePathname, useSearchParams } from "next/navigation";
import { Button, Tooltip } from "@trussworks/react-uswds";

import { USWDSIcon } from "src/components/USWDSIcon";

const SNACKBAR_VISIBLE_TIME = 4000;

type SearchSavedQueryProps = {
copyText: string;
copyingText: string;
copiedText: string;
helpText: string;
url: string;
snackbarMessage: React.ReactNode | string;
};
const SearchSavedQuery2 = ({ copyText, helpText }: SearchSavedQueryProps) => {

const SearchSavedQuery = ({
copyText,
copyingText,
copiedText,
helpText,
url,
snackbarMessage,
}: SearchSavedQueryProps) => {
const { copied, loading, copyToClipboard } = useCopyToClipboard();
const path = usePathname();
const searchParams = useSearchParams();
const url = `${environment.NEXT_PUBLIC_BASE_URL}${path}?${searchParams.toString()}`;
const { snackbarIsVisible, showSnackbar, Snackbar } = useSnackbar();

return (
<div className="text-underline border-base-lighter border-1px padding-2 text-primary-darker display-flex">
<Button
type="button"
unstyled
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick={() => copyToClipboard(url)}
>
<USWDSIcon name="content_copy" />
{loading ? "Copying..." : <>{copied ? "Copied!" : copyText}</>}
</Button>
<Tooltip
className="text-secondary-darker usa-button--unstyled"
label={helpText}
position="top"
>
<USWDSIcon className="margin-left-1" name="info_outline" />
</Tooltip>
<div>
<div className="text-underline border-base-lighter border-1px padding-2 text-primary-darker display-flex">
<Button
type="button"
unstyled
onClick={() => {
copyToClipboard(url, SNACKBAR_VISIBLE_TIME)
.then(() => {
showSnackbar(SNACKBAR_VISIBLE_TIME);
})
.catch((error) => {
console.error("Error copying to clipboard:", error);
});
}}
>
<USWDSIcon name="content_copy" />
{loading ? (
<>{copyingText}</>
) : (
<>{copied ? { copiedText } : copyText}</>
)}
</Button>
<Tooltip
className="text-secondary-darker usa-button--unstyled"
label={helpText}
position="top"
>
<USWDSIcon className="margin-left-1" name="info_outline" />
</Tooltip>
</div>
<Snackbar isVisible={snackbarIsVisible}>{snackbarMessage}</Snackbar>
</div>
);
};

export default SearchSavedQuery2;
export default SearchSavedQuery;
14 changes: 14 additions & 0 deletions frontend/src/components/user/SearchQuerySaveUserControl.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
"use client";

import { environment } from "src/constants/environments";

import { useTranslations } from "next-intl";
import dynamic from "next/dynamic";
import { usePathname, useSearchParams } from "next/navigation";

const SearchSavedQuery = dynamic(
() => import("src/components/search/SearchSavedQuery"),
{ ssr: false },
);

export const SearchQuerySaveUserControl = () => {
const path = usePathname();
const searchParams = useSearchParams();
const url = `${environment.NEXT_PUBLIC_BASE_URL}${path}?${searchParams.toString()}`;
const t = useTranslations("Search.savedQuery");
return (
<>
<SearchSavedQuery
copyText={t("copy.unauthenticated")}
helpText={t("help.unauthenticated")}
url={url}
snackbarMessage={t.rich("snackbar", {
br: () => (
<>
<br />
</>
),
})}
/>
</>
);
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/hooks/useCopyToClipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const useCopyToClipboard = () => {
const [copied, setCopied] = useState(false);
const [loading, setLoading] = useState(false);

const copyToClipboard = async (content: string) => {
const copyToClipboard = async (content: string, contentTime: number) => {
try {
setLoading(true);
await navigator.clipboard.writeText(content);
Expand All @@ -19,7 +19,7 @@ export const useCopyToClipboard = () => {
} finally {
setTimeout(() => {
setCopied(false);
}, 5000); // Reset copied state after 2 seconds
}, contentTime); // Reset copied state
}
};

Expand Down
18 changes: 18 additions & 0 deletions frontend/src/hooks/useSnackbar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client";

import { useState } from "react";

import Snackbar from "src/components/Snackbar";

export const useSnackbar = () => {
const [snackbarIsVisible, setSnackbarIsVisible] = useState<boolean>(false);

const showSnackbar = (visibleTime: number) => {
setSnackbarIsVisible(true);
setTimeout(() => {
setSnackbarIsVisible(false);
}, visibleTime);
};

return { snackbarIsVisible, showSnackbar, Snackbar };
};
4 changes: 4 additions & 0 deletions frontend/src/i18n/messages/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,10 +665,14 @@ export const messages = {
unauthenticated: "Copy this seach query",
authenticated: "Copy",
},
copying: "Copying",
copied: "Copied!",
help: {
unauthenticated:
"Use this set of search terms and filters often? Sign in to save this query to your account and use it again later. You can also copy and share the link to this query without signing in.",
},
snackbar:
"This search query was copied to your clipboard.<br></br> Paste it as a link anywhere.",
},
},
Maintenance: {
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/styles/_uswds-theme-custom-styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -260,16 +260,19 @@ i.e.
}
&--right {
&::after {
border-top-color: transparent;
border-right-color: color("secondary-darker");
}
}
&--left {
&::after {
border-top-color: transparent;
border-left-color: color("secondary-darker");
}
}
&--bottom {
&::after {
border-top-color: transparent;
border-bottom-color: color("secondary-darker");
}
}
Expand Down
23 changes: 23 additions & 0 deletions frontend/stories/components/search/SearchSavedQuery.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Meta } from "@storybook/react";

import SearchSavedQuery from "src/components/search/SearchSavedQuery";

const meta: Meta<typeof SearchSavedQuery> = {
title: "Components/Search/SearchSavedQuery",
component: SearchSavedQuery,
args: {
copyText: "Copy this link to your clipboard",
helpText: "This is a tooltip with some help text",
url: "https://www.example.com?query=example",
snackbarMessage: "This is the success message that will let you know",
},
};
export default meta;

export const Default = {
args: {
snackbarCopyText:
"This is the success message that will let you know. really. really.d",
snackbarMessage: "This is the success message that will let you knowddddd",
},
};

0 comments on commit ac34e4a

Please sign in to comment.