Skip to content

Commit

Permalink
feat: add status bar with icon's name and meta
Browse files Browse the repository at this point in the history
  • Loading branch information
Belar committed Sep 1, 2024
1 parent 4121e59 commit c1178fe
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
display: grid;

gap: 8px;

padding-bottom: 30px;
}
36 changes: 34 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ function App() {
defaultIconSetSettings,
);

const [hoveredIcon, setHoveredIcon] = useState<{
name: string;
library: {
name: string;
variant: string;
};
} | null>(null);

useEffect(() => {
const handleMessage = (event: MessageEvent<PluginMessageEvent>) => {
if (event.data.type === "theme") {
Expand Down Expand Up @@ -92,8 +100,13 @@ function App() {
icons: (typeof iconSets)[number]["icons"],
{
iconSettings: { svg: { attributes: customSvgAttributes = "" } = {} } = {},
}: Pick<(typeof iconSets)[number], "iconSettings">,
metadata,
}: Pick<(typeof iconSets)[number], "iconSettings"> & {
metadata: { library: { name: string; variant: string } };
},
) {
const { library } = metadata;

return Object.entries(icons)
.filter(([name]) => {
return name.toLowerCase().includes(searchPhrase.toLowerCase());
Expand All @@ -118,6 +131,8 @@ function App() {
key={`icon-${name}`}
label={`Insert icon: ${name}`}
onClick={() => handleIconButtonClick(name, svg)}
onMouseEnter={() => setHoveredIcon({ name, library })}
onMouseLeave={() => setHoveredIcon(null)}
>
{icon}
</IconButton>
Expand Down Expand Up @@ -182,6 +197,12 @@ function App() {
<GridList
items={generateIconList(icons, {
iconSettings,
metadata: {
library: {
name,
variant: iconSetsSettings[id].selectedVariant,
},
},
})}
emptyMessage={`No icons found for "${searchPhrase}" in ${name} library.`}
/>
Expand All @@ -191,6 +212,14 @@ function App() {
},
);

const metaBreadcrumbs = [
hoveredIcon?.library.name,
hoveredIcon?.library.variant,
hoveredIcon?.name,
]
.filter(Boolean)
.join(" > ");

function updateSettings(
id: string,
settings: Partial<
Expand All @@ -208,13 +237,16 @@ function App() {

return (
<div className="app" data-theme={theme}>
<ControlsBar stickToTop={true} growFirstItem={true}>
<ControlsBar stickTo={"top"} growFirstItem={true}>
<SearchInput
label="Search icon"
placeholder="e.g. arrow"
onChange={setSearchPhrase}
/>
</ControlsBar>
<ControlsBar stickTo={"bottom"}>
<p className="body-s">{metaBreadcrumbs || ""}</p>
</ControlsBar>
{iconGrids}
<p className="caption">
<sup>*</sup>Information about license is provided for informational
Expand Down
10 changes: 9 additions & 1 deletion src/ControlsBar.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@
align-items: center;
gap: 0.5rem;

min-height: 30px;
padding: var(--spacing-4) 0;
}

.controls-bar.grow-first > :first-child {
flex-grow: 1;
}

.controls-bar.sticky {
.controls-bar.sticky.top {
position: sticky;
top: 0;
}

.controls-bar.sticky.bottom {
position: fixed;
bottom: 0;

width: 100%;
}

[data-theme="dark"] .controls-bar {
background-color: var(--db-primary);
}
Expand Down
6 changes: 3 additions & 3 deletions src/ControlsBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import "./ControlsBar.css";

type ControlsBar = {
children: ReactNode;
stickToTop?: boolean;
stickTo?: "top" | "bottom";
growFirstItem?: boolean;
};

export default function ControlsBar({
children,
stickToTop,
stickTo,
growFirstItem,
}: ControlsBar) {
const className = `controls-bar ${stickToTop ? "sticky" : ""} ${growFirstItem ? "grow-first" : ""}`;
const className = `controls-bar ${stickTo && `sticky ${stickTo}`} ${growFirstItem ? "grow-first" : ""}`;

return <div className={className}>{children}</div>;
}
6 changes: 6 additions & 0 deletions src/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,26 @@ type IconButtonProps = {
children: ReactNode;
label: string;
onClick: (event: MouseEvent) => void;
onMouseEnter?: (event: MouseEvent) => void;
onMouseLeave?: (event: MouseEvent) => void;
size?: "compact";
};

export default function IconButton({
children: icon,
label,
onClick,
onMouseEnter,
onMouseLeave,
size,
}: IconButtonProps) {
return (
<button
className={`${size || ""}`}
data-appearance="secondary"
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{icon} <span className="visually-hidden">{label}</span>
</button>
Expand Down
42 changes: 35 additions & 7 deletions tests/icon.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { test, expect } from "@playwright/test";
import { setUpEventLogging } from "./utils/events";

const testIcon = {
name: "pickaxe",
svg: '<svg class="lucide lucide-pickaxe" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.531 12.469 6.619 20.38a1 1 0 1 1-3-3l7.912-7.912" /> <path d="M15.686 4.314A12.5 12.5 0 0 0 5.461 2.958 1 1 0 0 0 5.58 4.71a22 22 0 0 1 6.318 3.393" /> <path d="M17.7 3.7a1 1 0 0 0-1.4 0l-4.6 4.6a1 1 0 0 0 0 1.4l2.6 2.6a1 1 0 0 0 1.4 0l4.6-4.6a1 1 0 0 0 0-1.4z" /> <path d="M19.686 8.314a12.501 12.501 0 0 1 1.356 10.225 1 1 0 0 1-1.751-.119 22 22 0 0 0-3.393-6.319" /></svg>',
size: 24,
};
const testIcons = [
{
name: "a-arrow-down",
svg: '<svg class="lucide lucide-a-arrow-down" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3.5 13h6" /> <path d="m2 16 4.5-9 4.5 9" /> <path d="M18 7v9" /> <path d="m14 12 4 4 4-4" /></svg>',
size: 24,
},
{
name: "pickaxe",
svg: '<svg class="lucide lucide-pickaxe" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.531 12.469 6.619 20.38a1 1 0 1 1-3-3l7.912-7.912" /> <path d="M15.686 4.314A12.5 12.5 0 0 0 5.461 2.958 1 1 0 0 0 5.58 4.71a22 22 0 0 1 6.318 3.393" /> <path d="M17.7 3.7a1 1 0 0 0-1.4 0l-4.6 4.6a1 1 0 0 0 0 1.4l2.6 2.6a1 1 0 0 0 1.4 0l4.6-4.6a1 1 0 0 0 0-1.4z" /> <path d="M19.686 8.314a12.501 12.501 0 0 1 1.356 10.225 1 1 0 0 1-1.751-.119 22 22 0 0 0-3.393-6.319" /></svg>',
size: 24,
},
];

test.describe("icon", () => {
test("inserts an icon into a project", async ({ page }) => {
Expand All @@ -19,14 +26,35 @@ test.describe("icon", () => {
await iconSetToggleButton.click();

const iconButton = page.getByRole("button", {
name: `Insert icon: ${testIcon.name}`,
name: `Insert icon: ${testIcons[0].name}`,
});
await iconButton.click();

const event = eventLog.find(({ type }) => type === "insert-icon");
expect(event).toEqual({
type: "insert-icon",
content: testIcon,
content: testIcons[0],
});
});

test("on hover displays the icon name and library details in the status bar", async ({
page,
}) => {
await page.goto("/");

const iconSetToggleButton = page.getByRole("button", {
name: /Show Lucide icon set/,
});
await iconSetToggleButton.click();

for (const { name } of testIcons) {
const iconButton = page.getByRole("button", {
name: `Insert icon: ${name}`,
});
await iconButton.hover();

const statusByText = page.getByText(`Lucide > regular > ${name}`);
await expect(statusByText).toBeInViewport();
}
});
});

0 comments on commit c1178fe

Please sign in to comment.