Skip to content

Commit

Permalink
[PLAY-1397] Support Aliasing for Playbook-icons (#3479)
Browse files Browse the repository at this point in the history
**What does this PR do?** A clear and concise description with your
runway ticket url.
https://runway.powerhrg.com/backlog_items/PLAY-1397

Support aliasing for rails & react with a single JSON file as a source
of truth inside of Playbook.

**Screenshots:** Screenshots to visualize your addition/change


**How to test?** Steps to confirm the desired behavior:
1. For react, go to beta/icons.
2. For rails, go to kits/icon/rails


#### Checklist:
- [ ] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new
kit`, `deprecated`, or `breaking`. See [Changelog &
Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels)
for details.
- [ ] **DEPLOY** I have added the `milano` label to show I'm ready for a
review.
- [ ] **TESTS** I have added test coverage to my code.
  • Loading branch information
jasperfurniss committed Jun 27, 2024
1 parent 6556b71 commit c32dc2c
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -1,43 +1,8 @@
import React from "react"

import { linkFormat } from "../../../../../utilities/website_sidebar_helper"

import { Hero } from "../../components/Hero"
import {
Roofing,
Powergon,
Nitro,
ChevronDown,
Times,
Bars,
Calendar,
Filter,
Edit,
Trash,
Check,
Plus,
Search
} from '@powerhome/playbook-icons-react'

import { Body, Icon, Title, Flex, FlexItem, Card } from "playbook-ui"

const pbIcons = {
roofing: Roofing,
nitro: Nitro,
powergon: Powergon,
chevrondown: ChevronDown,
times: Times,
bars: Bars,
calendar: Calendar,
filter: Filter,
edit: Edit,
trash: Trash,
check: Check,
plus: Plus,
search: Search
}

window.PB_ICONS = pbIcons

export default function IconList() {

Expand Down Expand Up @@ -66,14 +31,14 @@ export default function IconList() {
hover={{ shadow: "deeper" }}
cursor='pointer'
>
<Icon icon="roofing" />
<Icon icon="circle-arrow-right" />
</Card>
<Card
marginRight='sm'
hover={{ shadow: "deeper" }}
cursor='pointer'
>
<Icon icon="powergon" />
<Icon icon="arrow-circle-right" />
</Card>
<Card
marginRight='sm'
Expand Down
14 changes: 14 additions & 0 deletions playbook-website/app/javascript/packs/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ import { Turbo } from "@hotwired/turbo-rails"
// Disable Turbo Drive by default (Turbo Drive is equivalent to Turbolinks)
Turbo.session.drive = false

// Icons from playbook-icons-react for testing
import * as icons from "@powerhome/playbook-icons-react"

window.PB_ICONS = {}

function pascalToKebabCase(str) {
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase()
}

Object.entries(icons).forEach(([key, value]) => {
const iconName = pascalToKebabCase(key)
window.PB_ICONS[iconName] = value
})


document.addEventListener('DOMContentLoaded', () => {
const anchors = new AnchorJS()
Expand Down
6 changes: 3 additions & 3 deletions playbook-website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
"release": "RAILS_ENV=production NODE_ENV=production ./bin/webpack"
},
"dependencies": {
"@fortawesome/fontawesome-pro": "^6.2.1",
"@fortawesome/fontawesome-pro": "6.2.1",
"@mapbox/mapbox-gl-draw": "^1.4.1",
"@powerhome/playbook-icons": "0.0.1-alpha.16",
"@powerhome/playbook-icons-react": "0.0.1-alpha.8",
"@powerhome/playbook-icons": "0.0.1-alpha.25",
"@powerhome/playbook-icons-react": "0.0.1-alpha.25",
"@powerhome/power-fonts": "0.0.1-alpha.4",
"@rails/webpacker": "5.4.3",
"@svgr/webpack": "5.5.0",
Expand Down
4 changes: 2 additions & 2 deletions playbook/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ test('generated custom Trigger', () => {
options={options}
>
<Dropdown.Trigger>
<Icon icon="home" />
<Icon icon="elephant" />
</Dropdown.Trigger>
{options.map((option) => (
<Dropdown.Option key={option.id}
Expand All @@ -182,7 +182,7 @@ test('generated custom Trigger', () => {

const kit = screen.getByTestId(testId)
const trigger = kit.querySelector('.pb_dropdown_trigger')
const customTrigger = trigger.querySelector('.fa-home.pb_icon_kit.fa-fw')
const customTrigger = trigger.querySelector('.fa-elephant.pb_icon_kit.fa-fw')
expect(customTrigger).toBeInTheDocument()
})

Expand Down
43 changes: 37 additions & 6 deletions playbook/app/pb_kits/playbook/pb_icon/_icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import classnames from 'classnames'
import { buildAriaProps, buildDataProps, buildHtmlProps } from '../utilities/props'
import { GlobalProps, globalProps } from '../utilities/globalProps'
import { isValidEmoji } from '../utilities/validEmojiChecker'
import aliasesJson from './icon_aliases.json'

export type IconSizes = "lg"
| "xs"
Expand Down Expand Up @@ -40,6 +41,19 @@ type IconProps = {
spin?: boolean,
} & GlobalProps

type AliasType = string | string[];

interface Aliases {
[key: string]: AliasType;
}

interface AliasesJson {
aliases: Aliases;
}

const aliases: AliasesJson = aliasesJson;


const flipMap = {
horizontal: 'fa-flip-horizontal',
vertical: 'fa-flip-vertical',
Expand All @@ -52,6 +66,22 @@ declare global {
var PB_ICONS: {[key: string]: React.FunctionComponent<any>}
}

// Resolve alias function
const resolveAlias = (icon: string): string => {
const alias = aliases.aliases[icon];

if (alias) {
if (Array.isArray(alias)) {
return alias[0];
} else {
return alias;
}
}

return icon;
};


const Icon = (props: IconProps) => {
const {
aria = {},
Expand All @@ -74,7 +104,8 @@ const Icon = (props: IconProps) => {
spin = false,
} = props

let iconElement: ReactSVGElement | null = typeof(icon) === "object" ? icon : null
const resolvedIcon = resolveAlias(icon as string)
let iconElement: ReactSVGElement | null = typeof(resolvedIcon) === "object" ? resolvedIcon : null

const faClasses = {
'fa-border': border,
Expand All @@ -90,12 +121,12 @@ const Icon = (props: IconProps) => {

if (!customIcon && !iconElement) {
const PowerIcon: React.FunctionComponent<any> | undefined =
window.PB_ICONS ? window.PB_ICONS[icon as string] : null
window.PB_ICONS ? window.PB_ICONS[resolvedIcon as string] : null

if (PowerIcon) {
iconElement = <PowerIcon /> as ReactSVGElement
} else {
faClasses[`fa-${icon}`] = icon as string
faClasses[`fa-${resolvedIcon}`] = resolvedIcon as string
}
}

Expand All @@ -115,7 +146,7 @@ const Icon = (props: IconProps) => {
className
)

aria.label ? null : aria.label = `${icon} icon`
aria.label ? null : aria.label = `${resolvedIcon} icon`
const ariaProps: {[key: string]: any} = buildAriaProps(aria)
const dataProps: {[key: string]: any} = buildDataProps(data)
const htmlProps = buildHtmlProps(htmlOptions)
Expand All @@ -137,7 +168,7 @@ const Icon = (props: IconProps) => {
}
</>
)
else if (isValidEmoji(icon as string))
else if (isValidEmoji(resolvedIcon as string))
return (
<>
<span
Expand All @@ -146,7 +177,7 @@ const Icon = (props: IconProps) => {
className={classesEmoji}
id={id}
>
{icon}
{resolvedIcon}
</span>
</>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
<%= pb_rails("icon", props: { icon: "user", fixed_width: true }) %>
<%= pb_rails("icon", props: { icon: "angles-down", fixed_width: true }) %>
<%= pb_rails("icon", props: { icon: "circle-arrow-right", fixed_width: true }) %>
<%= pb_rails("icon", props: { icon: "arrow-circle-right", fixed_width: true }) %>
<%= pb_rails("caption", props: { text: "Font Awesome (no alias & not in our Playbook-icons lib)", margin_y: "md" }) %>
<%= pb_rails("icon", props: { icon: "elephant", fixed_width: true }) %>
26 changes: 21 additions & 5 deletions playbook/app/pb_kits/playbook/pb_icon/docs/_icon_default.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
import React from 'react'
import React from "react"

import Icon from '../_icon'
import Icon from "../_icon"
import {Caption} from "../.."

const IconDefault = (props) => {
return (
<div>
<Icon
fixedWidth
icon="user"
<Icon fixedWidth
icon='angles-down'
{...props}
/>
<Icon fixedWidth
icon='circle-arrow-right'
{...props}
/>
<Icon fixedWidth
icon='arrow-circle-right'
{...props}
/>
<Caption
marginY='md'
text='Font Awesome (no alias & not in our Playbook-icons lib)'
/>
<Icon fixedWidth
icon='elephant'
{...props}
/>
</div>
Expand Down
22 changes: 21 additions & 1 deletion playbook/app/pb_kits/playbook/pb_icon/icon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# rubocop:disable Style/HashLikeCase

require "open-uri"
require "json"

module Playbook
module PbIcon
Expand Down Expand Up @@ -38,6 +39,8 @@ class Icon < Playbook::KitBase
prop :spin, type: Playbook::Props::Boolean,
default: false

ALIASES = JSON.parse(File.read(Playbook::Engine.root.join("app/pb_kits/playbook/pb_icon/icon_aliases.json")))["aliases"].freeze

def valid_emoji?
emoji_regex = /\p{Emoji}/
emoji_regex.match?(icon)
Expand Down Expand Up @@ -83,7 +86,8 @@ def asset_path
return unless Rails.application.config.respond_to?(:icon_path)

base_path = Rails.application.config.icon_path
icon_path = Dir.glob(Rails.root.join(base_path, "**", "#{icon}.svg")).first
resolved_icon = resolve_alias(icon)
icon_path = Dir.glob(Rails.root.join(base_path, "**", "#{resolved_icon}.svg")).first
icon_path if icon_path && File.exist?(icon_path)
end

Expand All @@ -106,6 +110,22 @@ def is_svg?

private

def resolve_alias(icon)
aliases = ALIASES[icon]
return icon unless aliases

if aliases.is_a?(Array)
aliases.find { |alias_name| file_exists?(alias_name) } || icon
else
aliases
end
end

def file_exists?(alias_name)
base_path = Rails.application.config.icon_path
File.exist?(Dir.glob(Rails.root.join(base_path, "**", "#{alias_name}.svg")).first)
end

def svg_size
size.nil? ? "1x" : size
end
Expand Down
39 changes: 39 additions & 0 deletions playbook/app/pb_kits/playbook/pb_icon/icon_aliases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"aliases": {
"arrow-alt-circle-right": "circle-right",
"angles-down": "angle-double-down",
"arrow-alt-down": "down",
"arrow-alt-up": "up",
"arrow-right-long": "long-arrow-right",
"arrow-to-bottom": "arrow-down-to-line",
"arrows-h": "arrows-left-right",
"calendar-days": "calendar-alt",
"circle-arrow-right": "arrow-circle-right",
"clock-rotate-left": "history",
"close": [
"times",
"xmark"
],
"ellipsis-h": "ellipsis",
"exclamation-circle": "circle-exclamation",
"external-link": "arrow-up-right-from-square",
"file-lines": "file-alt",
"gear": "cog",
"home": "house",
"info-circle": "circle-info",
"map-o": "map",
"message": "comment-alt",
"minus-circle": "circle-minus",
"money": "money-bill",
"mouse-pointer": "arrow-pointer",
"nitro": "nitro-n",
"play-circle": "circle-play",
"plus-circle": "circle-plus",
"plus-square": "square-plus",
"powergon": "powergon-p",
"question-circle": "circle-question",
"roofing": "product-roofing",
"shelves": "inventory",
"th-list": "table-list"
}
}
1 change: 1 addition & 0 deletions playbook/app/pb_kits/playbook/pb_icon/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module 'js-yaml'
20 changes: 10 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2201,9 +2201,9 @@
resolved "https://npm.powerapp.cloud/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.1.tgz#344baf6ff9eaad7a73cff067d8c56bfc11ae5304"
integrity sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A==

"@fortawesome/fontawesome-pro@^6.2.1":
"@fortawesome/fontawesome-pro@6.2.1":
version "6.2.1"
resolved "https://npm.powerapp.cloud/@fortawesome/fontawesome-pro/-/6.2.1/fontawesome-pro-6.2.1.tgz#1cd5b79d93fdb955ef8a04996fef20a44207a6f0"
resolved "https://npm.powerapp.cloud/@fortawesome/fontawesome-pro/-/fontawesome-pro-6.2.1.tgz#1cd5b79d93fdb955ef8a04996fef20a44207a6f0"
integrity sha512-dHk7iiCf5MCmoUvmrYzfN/I3gebpgpA1oqlOffgOThnsaAR4kpaQ5YMTdkdG99Of1hnN0Bok6R+HE28zXb3SOg==

"@gar/promisify@^1.0.1":
Expand Down Expand Up @@ -2832,15 +2832,15 @@
resolved "https://npm.powerapp.cloud/@powerhome/eslint-config/-/eslint-config-0.1.0.tgz#f1e1151481f51421186ba8caad99fadb24b1fe51"
integrity sha512-8e5DRgnKPbJ+ofAtqjUPZTxcgHg/TVf52WeNqAGBUXSS4QWHemL6fj1n/YHfAIvx3aMUhFwrw2lUNofUocgK3A==

"@powerhome/playbook-icons-react@0.0.1-alpha.8":
version "0.0.1-alpha.8"
resolved "https://npm.powerapp.cloud/@powerhome/playbook-icons-react/-/d71ffd31679c57b3a7358718ce84b2f9e5f0c577#d71ffd31679c57b3a7358718ce84b2f9e5f0c577"
integrity sha512-SUM1/MsJb4nnNEz2A9PMXa8hgBeCdGCNQWEdrozy1l2/1GZOkiw28aGIUZZN3LAR2+0hRRNhVhcetfpuGCZcFg==
"@powerhome/playbook-icons-react@0.0.1-alpha.25":
version "0.0.1-alpha.25"
resolved "https://npm.powerapp.cloud/@powerhome/playbook-icons-react/-/3a700f9b6d6427aefe0fb94039e292cea462fe9e#3a700f9b6d6427aefe0fb94039e292cea462fe9e"
integrity sha512-0h53TdW/PrUrwYFxonE0dIN9vupqAVLwBv70hSrRkrKb8chuLc/+kBtL15MeSJ+9cWIXF0ocFOHBlNTc25hmyw==

"@powerhome/playbook-icons@0.0.1-alpha.16":
version "0.0.1-alpha.16"
resolved "https://npm.powerapp.cloud/@powerhome/playbook-icons/-/273baad6202de2736f9e38287e5589e00f47dfb8#273baad6202de2736f9e38287e5589e00f47dfb8"
integrity sha512-Kim7OqGxotUuhcxHBQI8a1M184DvzSyHU3ZmN/B6JANcw2jIOVL9z0f+qa9Trgij1cmyY444WC51LDbYaGvEug==
"@powerhome/playbook-icons@0.0.1-alpha.25":
version "0.0.1-alpha.25"
resolved "https://npm.powerapp.cloud/@powerhome/playbook-icons/-/261d2f40561f1d4d0505e4af18892916941ed1a0#261d2f40561f1d4d0505e4af18892916941ed1a0"
integrity sha512-LtnN8E/yHaXzjQG+sZfpUbGA9Xdqpwhx8aFSPvg0lGf7phOoNFHSmjXhK0a36bZnPn0tIGBct9LhSzp6zhyVBA==

"@powerhome/power-fonts@0.0.1-alpha.4":
version "0.0.1-alpha.4"
Expand Down

0 comments on commit c32dc2c

Please sign in to comment.