Skip to content

[WIP] Standardize FontAwesome usage #20334

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

Draft
wants to merge 4 commits into
base: dev
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion client/docs/headings.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ Following properties allow for further styling the component:
- `bold` - makes a heading bold
- `inline` - displays the heading inline
- `separator` - draws a separating line, to better distinguish sections
- `icon="..."` - adds a font-awesome icon decoration to the left of the heading. Make sure to also load the icon with `library.add(...)`.
- `icon={iconObject}` - adds an icon to the left of the heading. Import from FontAwesome (`@fortawesome/free-solid-svg-icons`) or Galaxy icons (`@/components/icons/galaxyIcons`).
2 changes: 2 additions & 0 deletions client/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ function stageLibs(callback) {

async function icons() {
await buildIcons("./src/assets/icons.json");
const generateIconsTypeScript = require("./icons/generate_ts_icons");
generateIconsTypeScript();
}

function stagePlugins(callback) {
Expand Down
22 changes: 17 additions & 5 deletions client/icons/all-icons.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
# Icons

All custom icons available
Galaxy custom icons

This file is auto-generated by `build_icons.js`. Manual changes will be lost!
Auto-generated by `build_icons.js`

## Usage

```vue
<script setup>
import { galaxyLogo } from "@/components/icons/galaxyIcons";
</script>

<template>
<FontAwesomeIcon :icon="galaxyLogo" />
</template>
```

---

- `<FontAwesomeIcon :icon="['gxd', 'galaxyLogo']" />`
- `<FontAwesomeIcon :icon="['gxd', 'textLarger']" />`
- `<FontAwesomeIcon :icon="['gxd', 'textSmaller']" />`
- **galaxyLogo**: `import { galaxyLogo } from "@/components/icons/galaxyIcons";`
- **textLarger**: `import { textLarger } from "@/components/icons/galaxyIcons";`
- **textSmaller**: `import { textSmaller } from "@/components/icons/galaxyIcons";`

---
15 changes: 12 additions & 3 deletions client/icons/build_icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,21 @@ function findAllPaths(element) {

function makeMarkdown(iconDefinitions) {
let content = "# Icons\n\n";
content += "All custom icons available\n\n";
content += "This file is auto-generated by `build_icons.js`. Manual changes will be lost!\n\n";
content += "Galaxy custom icons\n\n";
content += "Auto-generated by `build_icons.js`\n\n";
content += "## Usage\n\n";
content += "```vue\n";
content += "<script setup>\n";
content += 'import { galaxyLogo } from "@/components/icons/galaxyIcons";\n';
content += "</script>\n\n";
content += "<template>\n";
content += ' <FontAwesomeIcon :icon="galaxyLogo" />\n';
content += "</template>\n";
content += "```\n\n";
content += "---\n\n";

iconDefinitions.forEach((icon) => {
content += `- \`<FontAwesomeIcon :icon="['${icon.prefix}', '${icon.iconName}']" />\`\n`;
content += `- **${icon.iconName}**: \`import { ${icon.iconName} } from "@/components/icons/galaxyIcons";\`\n`;
});

content += "\n---\n";
Expand Down
76 changes: 76 additions & 0 deletions client/icons/generate_ts_icons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const fs = require("fs");
const path = require("path");

/**
* Generate TypeScript exports for Galaxy custom icons
*/
function generateIconsTypeScript() {
// Read the generated icons JSON
const iconsJsonPath = path.resolve(__dirname, "../src/assets/icons.json");
const outputPath = path.resolve(__dirname, "../src/components/icons/galaxyIcons.ts");

if (!fs.existsSync(iconsJsonPath)) {
console.error(`Icons JSON not found at ${iconsJsonPath}`);
console.error("Run 'npm run icons' first to generate the icons JSON");
return;
}

const iconsJson = fs.readFileSync(iconsJsonPath, "utf8");
const icons = JSON.parse(iconsJson);

// Generate TypeScript content
let content = `import type { IconDefinition } from "@fortawesome/fontawesome-svg-core";

// Galaxy custom icons
// Generated from: ${iconsJsonPath}
// To regenerate, run: npm run icons

// Matches FontAwesome's structure
export interface GalaxyIconDefinition {
iconName: string;
prefix: string;
icon: [number, number, never[], string, string | string[]];
}

// Accepts both FA and Galaxy icons
export type IconLike = IconDefinition | GalaxyIconDefinition;

`;

icons.forEach((icon) => {
// Convert icon name to camelCase for JavaScript export
const exportName = icon.iconName;

// Format the icon data manually to match the expected style
const iconData = icon.icon;
const formattedIcon = `{
iconName: "${icon.iconName}",
prefix: "${icon.prefix}",
icon: [
${iconData[0]},
${iconData[1]},
[],
"",
[
${iconData[4].map((path) => `"${path}"`).join(",\n ")},
],
],
};`;

content += `export const ${exportName}: GalaxyIconDefinition = ${formattedIcon}

`;
});

// Write the TypeScript file
fs.writeFileSync(outputPath, content.trimEnd() + "\n");
console.log(`Generated Galaxy icons TypeScript exports at: ${outputPath}`);
console.log(`Exported ${icons.length} icons: ${icons.map((i) => i.iconName).join(", ")}`);
}

// Run if called directly
if (require.main === module) {
generateIconsTypeScript();
}

module.exports = generateIconsTypeScript;
3 changes: 2 additions & 1 deletion client/src/components/AboutGalaxy.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { computed } from "vue";
import { RouterLink } from "vue-router";

import { galaxyLogo } from "@/components/icons/galaxyIcons";
import { useConfig } from "@/composables/config";
import { getAppRoot } from "@/onload/loadConfig";

Expand All @@ -29,7 +30,7 @@ const versionUserDocumentationUrl = computed(() => {

<template>
<div v-if="isConfigLoaded" class="about-galaxy">
<Heading h1 :icon="['gxd', 'galaxyLogo']" size="lg">Help and Support</Heading>
<Heading h1 :icon="galaxyLogo" size="lg">Help and Support</Heading>
<div class="p-2">
<Heading h2 separator size="md">Support</Heading>
<div v-if="config.wiki_url">
Expand Down
3 changes: 0 additions & 3 deletions client/src/components/Citation/CitationItem.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { computed } from "vue";

import type { Citation } from ".";

library.add(faExternalLinkAlt);

interface Props {
citation: Citation;
outputFormat?: string;
Expand Down Expand Up @@ -38,7 +35,7 @@
<template>
<div>
{{ prefix }}
<span v-html="citationHtml" />

Check warning on line 38 in client/src/components/Citation/CitationItem.vue

View workflow job for this annotation

GitHub Actions / client-unit-test

'v-html' directive can lead to XSS attack

<a v-if="link" :href="link" target="_blank">
Visit reference
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
<script lang="ts">
/* cannot use a setup block and get params injection in Vue 2.7 I think */

import { library } from "@fortawesome/fontawesome-svg-core";
import { faTimes } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import type { ICellRendererParams } from "ag-grid-community";
import { defineComponent } from "vue";

library.add(faTimes);

export default defineComponent({
components: {
FontAwesomeIcon,
},
data() {
return {
params: {} as ICellRendererParams,
faTimes,
};
},
computed: {},
Expand All @@ -28,7 +26,7 @@ export default defineComponent({
</script>
<template>
<div>
<FontAwesomeIcon size="2x" icon="fa-times" @click="onRemove" />
<FontAwesomeIcon size="2x" :icon="faTimes" @click="onRemove" />
</div>
</template>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
<script lang="ts">
/* cannot use a setup block and get params injection in Vue 2.7 I think */

import { library } from "@fortawesome/fontawesome-svg-core";
import { faCheck, faExclamationTriangle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import type { ICellRendererParams } from "ag-grid-community";
import { BPopover } from "bootstrap-vue";
import { defineComponent } from "vue";

library.add(faCheck, faExclamationTriangle);

export default defineComponent({
components: {
BPopover,
Expand All @@ -18,6 +15,8 @@ export default defineComponent({
data() {
return {
params: {} as ICellRendererParams,
faCheck,
faExclamationTriangle,
};
},
computed: {
Expand Down Expand Up @@ -48,9 +47,9 @@ export default defineComponent({
},
icon() {
if (this.isDuplicate || this.requiresPairing) {
return "fa-exclamation-triangle";
return this.faExclamationTriangle;
} else {
return "fa-check";
return this.faCheck;
}
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faBars, faCog, faDatabase, faSave, faTable, faUser } from "@fortawesome/free-solid-svg-icons";
import { faBars, faCog, faDatabase, faSave, faTable } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import axios from "axios";
import { BAlert, BSpinner, BTab, BTabs } from "bootstrap-vue";
Expand All @@ -26,8 +25,6 @@ import Heading from "@/components/Common/Heading.vue";
import FormDisplay from "@/components/Form/FormDisplay.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";

library.add(faBars, faCog, faDatabase, faSave, faTable, faUser);

interface Props {
collectionId: string;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
/* cannot use a setup block and get params injection in Vue 2.7 I think */

import { library } from "@fortawesome/fontawesome-svg-core";
import { faLink, faUndo, faUnlink } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import type { ICellRendererParams } from "ag-grid-community";
Expand All @@ -13,8 +12,6 @@

type StoreType = ReturnType<typeof usePairingDatasetTargetsStore>;

library.add(faLink, faUndo, faUnlink);

export default defineComponent({
components: {
FontAwesomeIcon,
Expand All @@ -23,6 +20,9 @@
return {
params: {} as ICellRendererParams,
showHid: showHid,
faLink,
faUndo,
faUnlink,
};
},
computed: {
Expand Down Expand Up @@ -97,8 +97,8 @@
<template>
<div>
<div v-if="isPaired" class="paired-datasets-cell">
<FontAwesomeIcon size="2x" icon="fa-undo" class="paired-action-icon" @click="onSwap" />
<FontAwesomeIcon size="2x" icon="fa-unlink" class="paired-action-icon" @click="onUnpair" />
<FontAwesomeIcon size="2x" :icon="faUndo" class="paired-action-icon" @click="onSwap" />
<FontAwesomeIcon size="2x" :icon="faUnlink" class="paired-action-icon" @click="onUnpair" />
<div class="text-container">
<span>
<span class="direction">FORWARD</span>
Expand All @@ -113,7 +113,7 @@
</div>
</div>
<div v-else class="paired-datasets-cell">
<div

Check warning on line 116 in client/src/components/Collections/common/PairedDatasetCellComponent.vue

View workflow job for this annotation

GitHub Actions / client-unit-test

Visible, non-interactive elements with click handlers must have at least one keyboard listener

Check warning on line 116 in client/src/components/Collections/common/PairedDatasetCellComponent.vue

View workflow job for this annotation

GitHub Actions / client-unit-test

Visible, non-interactive elements should not have an interactive handler
class="icon-wrapper"
:class="{ dragging: dragging, 'drop-target': dropOver }"
:draggable="true"
Expand All @@ -126,7 +126,7 @@
@drop="onDrop">
<FontAwesomeIcon
size="2x"
icon="fa-link"
:icon="faLink"
class="paired-action-icon"
:class="{ dragging: dragging, 'active-target': isUnpairedTarget, 'fa-beat': isUnpairedTarget }" />
</div>
Expand Down
3 changes: 0 additions & 3 deletions client/src/components/Common/DelayedInput.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faAngleDoubleDown, faAngleDoubleUp, faSpinner, faTimes } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { watchImmediate } from "@vueuse/core";
Expand All @@ -8,8 +7,6 @@ import { ref, watch } from "vue";

import localize from "@/utils/localization";

library.add(faAngleDoubleDown, faAngleDoubleUp, faSpinner, faTimes);

interface Props {
value?: string;
delay?: number;
Expand Down
3 changes: 0 additions & 3 deletions client/src/components/Common/ExportRecordDetails.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faCheckCircle,
faClock,
Expand All @@ -18,8 +17,6 @@ import GButton from "@/components/BaseComponents/GButton.vue";
import Heading from "@/components/Common/Heading.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";

library.add(faCheckCircle, faClock, faExclamationCircle, faExclamationTriangle, faLink);

interface Props {
record: ExportRecord;
objectType: string;
Expand Down
3 changes: 0 additions & 3 deletions client/src/components/Common/ExportRecordTable.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faCheckCircle,
faDownload,
Expand All @@ -17,8 +16,6 @@ import type { ExportRecord } from "./models/exportRecordModel";
import GButton from "@/components/BaseComponents/GButton.vue";
import GButtonGroup from "@/components/BaseComponents/GButtonGroup.vue";

library.add(faCheckCircle, faDownload, faExclamationCircle, faFileImport, faLink, faSpinner);

interface Props {
records: ExportRecord[];
}
Expand Down
3 changes: 0 additions & 3 deletions client/src/components/Common/FilterMenu.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faAngleDoubleUp, faQuestion, faSearch } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BModal, BPopover } from "bootstrap-vue";
Expand All @@ -18,8 +17,6 @@ import FilterMenuMultiTags from "@/components/Common/FilterMenuMultiTags.vue";
import FilterMenuObjectStore from "@/components/Common/FilterMenuObjectStore.vue";
import FilterMenuRanged from "@/components/Common/FilterMenuRanged.vue";

library.add(faAngleDoubleUp, faQuestion, faSearch);

interface BackendFilterError {
err_msg: string;
err_code: number;
Expand Down
3 changes: 0 additions & 3 deletions client/src/components/Common/FilterMenuDropdown.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faQuestion } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton, BDropdown, BDropdownItem, BInputGroup, BInputGroupAppend, BModal } from "bootstrap-vue";
Expand All @@ -12,8 +11,6 @@ import { errorMessageAsString } from "@/utils/simple-error";

import QuotaUsageBar from "@/components/User/DiskUsage/Quota/QuotaUsageBar.vue";

library.add(faQuestion);

type FilterValue = QuotaUsage | string | boolean | undefined;

type DatalistItem = { value: string; text: string };
Expand Down
Loading
Loading