Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import "../styles/app.css";
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faChevronLeft,
faChevronRight,
faEnvelope,
faUser,
} from "@fortawesome/free-solid-svg-icons";

// Demo icons the stories use; like any app, Storybook registers its own icons
library.add(faChevronLeft, faChevronRight, faEnvelope, faUser);

export const parameters = {
controls: {
Expand Down
23 changes: 18 additions & 5 deletions src/Icon/Icon.stories.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faRocket } from "@fortawesome/free-solid-svg-icons";
import { faGithub } from "@fortawesome/free-brands-svg-icons";
import { D9Icon } from "../index";
import { icons, brands } from "../index";

// Neither icon is in D9's own list: registered here the way an app does
library.add(faRocket, faGithub);

export default {
title: "Basics/Icon",
Expand All @@ -9,10 +14,6 @@ export default {
options: ["xs", "1x", "2x", "3x", "4x"],
control: { type: "inline-radio" },
},
name: {
options: [...icons, ...brands],
control: { type: "select" },
},
},
decorators: [
() => ({
Expand Down Expand Up @@ -47,3 +48,15 @@ Large.args = {
name: "user",
size: "2x",
};

export const AppRegistered = Template.bind({});
AppRegistered.args = {
name: "rocket",
size: "2x",
};

export const AppRegisteredBrand = Template.bind({});
AppRegisteredBrand.args = {
name: "github",
size: "2x",
};
33 changes: 15 additions & 18 deletions src/Icon/Icon.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<template>
<font-awesome-icon
v-if="isIconAvailable"
:icon="[iconBag, name]"
v-bind="$attrs"
/>
<font-awesome-icon v-if="definition" :icon="definition" v-bind="$attrs" />
</template>

<script setup lang="ts">
import {
findIconDefinition,
IconName,
} from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { withDefaults, computed } from "vue";
import icons, { brands } from "./registerIcons";
import "./registerIcons";

interface IconProps {
name: string;
Expand All @@ -19,23 +19,20 @@ const props = withDefaults(defineProps<IconProps>(), {
name: "user",
});

const iconBag = computed((): "fab" | "fas" => {
if (brands.includes(props.name)) {
return "fab";
}

return "fas";
});
// Looked up in the library at render, so icons an app adds with library.add work too
const definition = computed(() => {
const iconName = props.name as IconName;
const found =
findIconDefinition({ prefix: "fab", iconName }) ??
findIconDefinition({ prefix: "fas", iconName });

const isIconAvailable = computed((): boolean => {
if (icons.includes(props.name) || brands.includes(props.name)) {
return true;
} else {
if (!found) {
console.warn(
"could not load icon, because it is not available",
props.name
);
return false;
}

return found;
});
</script>
15 changes: 0 additions & 15 deletions src/Icon/custom/numeric-input.d.ts

This file was deleted.

29 changes: 0 additions & 29 deletions src/Icon/custom/numeric-input.js

This file was deleted.

146 changes: 19 additions & 127 deletions src/Icon/registerIcons.ts
Original file line number Diff line number Diff line change
@@ -1,132 +1,24 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import * as fasIcons from "@fortawesome/free-solid-svg-icons";
import * as fabIcon from "@fortawesome/free-brands-svg-icons";
import numericInput from "./custom/numeric-input.js";
import {
faCheck,
faChevronDown,
faCircleNotch,
faEllipsis,
faEyeDropper,
} from "@fortawesome/free-solid-svg-icons";

library.add(
fasIcons.faAlignCenter,
fasIcons.faAlignLeft,
fasIcons.faAlignRight,
fasIcons.faArrowRight,
fasIcons.faArrowUp19,
fasIcons.faAsterisk,
fasIcons.faAt,
fasIcons.faBold,
fasIcons.faBolt,
fasIcons.faCalendar,
fasIcons.faChartPie,
fasIcons.faCheck,
fasIcons.faCheckCircle,
fasIcons.faChevronDown,
fasIcons.faChevronLeft,
fasIcons.faChevronRight,
fasIcons.faChevronUp,
fasIcons.faCircleCheck,
fasIcons.faCircleNodes,
fasIcons.faCircleNotch,
fasIcons.faClipboard,
fasIcons.faCloudDownload,
fasIcons.faCloudUpload,
fasIcons.faCode,
fasIcons.faCodeBranch,
fasIcons.faCodeFork,
fasIcons.faCog,
fasIcons.faCompass,
fasIcons.faCrown,
fasIcons.faDiagramProject,
fasIcons.faDoorOpen,
fasIcons.faDotCircle,
fasIcons.faEllipsis,
fasIcons.faEllipsisVertical,
fasIcons.faEnvelope,
fasIcons.faExclamationTriangle,
fasIcons.faExternalLink,
fasIcons.faEye,
fasIcons.faEyeDropper,
fasIcons.faEyeSlash,
fasIcons.faFileLines,
fasIcons.faFilePdf,
fasIcons.faFileUpload,
fasIcons.faFileVideo,
fasIcons.faFileWaveform,
fasIcons.faFilter,
fasIcons.faFilter,
fasIcons.faFingerprint,
fasIcons.faFont,
fasIcons.faGears,
fasIcons.faGlobe,
fasIcons.faGripHorizontal,
fasIcons.faGripVertical,
fasIcons.faHandPointRight,
fasIcons.faHandPointRight,
fasIcons.faHashtag,
fasIcons.faHeading,
fasIcons.faHeart,
fasIcons.faImage,
fasIcons.faInfoCircle,
fasIcons.faItalic,
fasIcons.faLayerGroup,
fasIcons.faLink,
fasIcons.faLinkSlash,
fasIcons.faList,
fasIcons.faListOl,
fasIcons.faListUl,
fasIcons.faLocationArrow,
fasIcons.faLocationCrosshairs,
fasIcons.faLock,
fasIcons.faLockOpen,
fasIcons.faMedal,
fasIcons.faMessage,
fasIcons.faMicrochip,
fasIcons.faPencilAlt,
fasIcons.faPenToSquare,
fasIcons.faPhone,
fasIcons.faPlayCircle,
fasIcons.faPlus,
fasIcons.faPlusCircle,
fasIcons.faPoo,
fasIcons.faPuzzlePiece,
fasIcons.faQuestion,
fasIcons.faQuoteLeft,
fasIcons.faQuoteRight,
fasIcons.faReply,
fasIcons.faReplyAll,
fasIcons.faRightLong,
fasIcons.faRobot,
fasIcons.faSave,
fasIcons.faShield,
fasIcons.faSignal,
fasIcons.faSignature,
fasIcons.faSignature,
fasIcons.faSquareCheck,
fasIcons.faStar,
fasIcons.faStrikethrough,
fasIcons.faTerminal,
fasIcons.faTextSlash,
fasIcons.faTimes,
fasIcons.faTrash,
fasIcons.faTree,
fasIcons.faTrophy,
fasIcons.faTurnDown,
fasIcons.faUnderline,
fasIcons.faUpload,
fasIcons.faUser,
fasIcons.faUserShield,
fasIcons.faWandMagicSparkles,
// Only the icons D9 components render themselves; apps register every other icon with library.add
const definitions = [
faCheck,
faChevronDown,
faCircleNotch,
faEllipsis,
faEyeDropper,
];

// Brands
fabIcon.faTwitter,
fabIcon.faInstagram,
fabIcon.faGithub,
fabIcon.faLinkedin,
fabIcon.faFacebook,
library.add(...definitions);

// Custom
numericInput.definition
);
export default definitions.map(({ iconName }): string => iconName);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The Button, Select and Input stories use icons as their icon control options (Button.stories.ts:17, Select.stories.ts:13, Input.stories.ts:19). That list now holds only these 5, not the demo icons the stories default to. Add the demo names, or drop the options as in Icon.stories.ts.


// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default [...Object.keys((library as any).definitions.fas)].sort();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const brands = [...Object.keys((library as any).definitions.fab)].sort();
// Kept for apps that import it; D9 registers no brand icons
export const brands: string[] = [];
10 changes: 5 additions & 5 deletions tests/Icon.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faGithub } from "@fortawesome/free-brands-svg-icons";
import { faGitlab } from "@fortawesome/free-brands-svg-icons";
import { mount } from "@vue/test-utils";
import { describe, expect, it, vi } from "vitest";
import { D9Icon } from "../src";

describe("D9Icon", () => {
it("renders a brand icon with the fab prefix", () => {
// Registered here like an app does, so the spec doesn't rely on D9's own icon list
library.add(faGithub);
const wrapper = mount(D9Icon, { props: { name: "github" } });
// Registered here like an app does; GitLab was never in D9's own icon list
library.add(faGitlab);
const wrapper = mount(D9Icon, { props: { name: "gitlab" } });
const svg = wrapper.find("svg");

expect(svg.attributes("data-prefix")).toBe("fab");
expect(svg.attributes("data-icon")).toBe("github");
expect(svg.attributes("data-icon")).toBe("gitlab");
});

it("renders nothing for an unknown name", () => {
Expand Down
Loading