Skip to content

Commit 57f235b

Browse files
authored
[PLAY-1766] Icon button rails (#4141)
**What does this PR do?** A clear and concise description with your runway ticket url. Runway https://runway.powerhrg.com/backlog_items/PLAY-1766 Adds a new rails only kit for a square style icon button Its very similar to the circle icon button Its still in beta, just made a "default" and "link" variant Check it out here https://pr4141.playbook.beta.px.powerapp.cloud/kits/icon_button ![screenshot-pr4141_playbook_beta_px_powerapp_cloud-2025_01_21-10_09_31](https://github.com/user-attachments/assets/0d918cf3-92d8-4f86-9f6c-2acbdd893f13)
1 parent ab73ea9 commit 57f235b

File tree

8 files changed

+194
-0
lines changed

8 files changed

+194
-0
lines changed

playbook-website/config/menu.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ kits:
8888
icons_used: true
8989
react_rendered: false
9090
enhanced_element_used: false
91+
- name: icon_button
92+
platforms: rails_only
93+
status: beta
94+
icons_used: true
95+
react_rendered: false
96+
enhanced_element_used: false
9197
- category: data_visualization
9298
description:
9399
components:

playbook/app/entrypoints/playbook.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
@import 'kits/pb_advanced_table/advanced_table';
23
@import 'kits/pb_avatar/avatar';
34
@import 'kits/pb_avatar_action_button/avatar_action_button';
@@ -108,6 +109,7 @@
108109
@import 'kits/pb_user_badge/user_badge';
109110
@import 'kits/pb_walkthrough/walkthrough';
110111
@import 'kits/pb_weekday_stacked/weekday_stacked';
112+
@import 'kits/pb_icon_button/icon_button';
111113
@import 'utilities/mixins';
112114
@import 'utilities/spacing';
113115
@import 'utilities/cursor';
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
$icon_colors: (
2+
default: $text_lt_default,
3+
link: $primary_action
4+
);
5+
6+
@mixin icon_button_variant($variant) {
7+
.icon_button_icon {
8+
color: map-get($icon_colors, $variant);
9+
}
10+
}
11+
12+
.pb_icon_button_kit_default,
13+
.pb_icon_button_kit_link {
14+
width: fit-content;
15+
height: fit-content;
16+
17+
&:hover {
18+
[class*="pb_button_kit"] {
19+
background-color: $bg_light;
20+
}
21+
.icon_button_icon {
22+
color: $primary_action;
23+
}
24+
}
25+
26+
&:active {
27+
[class*="pb_button_kit"] {
28+
background-color: $bg_light;
29+
}
30+
.icon_button_icon {
31+
color: $primary_action;
32+
}
33+
}
34+
35+
&:hover:active {
36+
[class*="pb_button_kit"] {
37+
background-color: $bg_light;
38+
}
39+
.icon_button_icon {
40+
color: $text_lt_default;
41+
}
42+
}
43+
44+
&:focus {
45+
outline: 2px solid $primary_action;
46+
border-radius: 8px;
47+
}
48+
49+
[class*="pb_button_kit"] {
50+
min-height: 0;
51+
background: none;
52+
position: relative;
53+
display: flex;
54+
justify-content: center;
55+
align-items: center;
56+
flex-shrink: 0;
57+
flex-grow: 0;
58+
width: fit-content;
59+
height: fit-content;
60+
line-height: normal;
61+
flex-basis: auto;
62+
min-width: 0;
63+
padding: 0;
64+
65+
.icon_button_icon {
66+
display: block;
67+
vertical-align: middle;
68+
}
69+
}
70+
}
71+
72+
.pb_icon_button_kit_default {
73+
@include icon_button_variant(default);
74+
}
75+
76+
.pb_icon_button_kit_link {
77+
@include icon_button_variant(link);
78+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<%= pb_rails("icon_button") %>
2+
</br>
3+
<%= pb_rails("icon_button", props: {variant: "link"}) %>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
examples:
2+
3+
rails:
4+
- icon_button_default: Default
5+
6+
7+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<%= pb_content_tag do %>
2+
<%= pb_rails("button", props: { type: object.type,
3+
link: object.link,
4+
new_window:object.new_window,
5+
target: object.target,
6+
dark: object.dark,
7+
border_radius: "lg" }) do %>
8+
<%= pb_rails("icon", props: { icon: object.icon,
9+
fixed_width: true,
10+
dark: object.dark,
11+
size: "2x",
12+
color: "text_lt_default",
13+
classname: "icon_button_icon",
14+
padding_x: "xxs", padding_y: "xs" }) %>
15+
<% end %>
16+
<% end %>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
module Playbook
4+
module PbIconButton
5+
class IconButton < ::Playbook::KitBase
6+
prop :type, type: Playbook::Props::Enum,
7+
values: %w[button submit reset],
8+
default: "button"
9+
prop :icon, required: false, default: "bars"
10+
prop :link
11+
prop :new_window, type: Playbook::Props::Boolean,
12+
default: false
13+
prop :target
14+
prop :variant, type: Playbook::Props::Enum,
15+
values: %w[default link],
16+
default: "default"
17+
def classname
18+
generate_classname("pb_icon_button_kit", variant)
19+
end
20+
end
21+
end
22+
end
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../../../../app/pb_kits/playbook/pb_icon_button/icon_button"
4+
5+
RSpec.describe Playbook::PbIconButton::IconButton do
6+
subject { Playbook::PbIconButton::IconButton }
7+
8+
# Test prop definitions
9+
it { is_expected.to define_prop(:icon).with_default("bars") }
10+
it { is_expected.to define_prop(:link) }
11+
it { is_expected.to define_boolean_prop(:new_window).with_default(false) }
12+
it {
13+
is_expected.to define_enum_prop(:type)
14+
.with_default("button")
15+
.with_values("button", "submit", "reset")
16+
}
17+
it {
18+
is_expected.to define_enum_prop(:variant)
19+
.with_default("default")
20+
.with_values("default", "link")
21+
}
22+
23+
# Test classname generation
24+
describe "#classname" do
25+
it "returns the correct namespaced class name", :aggregate_failures do
26+
expect(subject.new(variant: "default").classname).to eq "pb_icon_button_kit_default"
27+
expect(subject.new(variant: "link").classname).to eq "pb_icon_button_kit_link"
28+
expect(subject.new(classname: "additional_class").classname).to eq "pb_icon_button_kit_default additional_class"
29+
end
30+
31+
it "raises an error when given an invalid variant" do
32+
expect { subject.new(variant: "invalid_variant") }.to raise_error(Playbook::Props::Error)
33+
end
34+
end
35+
36+
# Test rendering behavior
37+
describe "rendering" do
38+
let(:icon) { "bars" }
39+
40+
it "renders the default variant with icon" do
41+
result = subject.new(icon: icon).classname
42+
expect(result).to eq "pb_icon_button_kit_default"
43+
end
44+
45+
it "renders the link variant" do
46+
result = subject.new(icon: icon, variant: "link").classname
47+
expect(result).to eq "pb_icon_button_kit_link"
48+
end
49+
50+
it "renders a button with type submit" do
51+
result = subject.new(icon: icon, type: "submit").classname
52+
expect(result).to include "pb_icon_button_kit_default"
53+
end
54+
55+
it "renders with custom classname" do
56+
result = subject.new(icon: icon, classname: "extra_class").classname
57+
expect(result).to eq "pb_icon_button_kit_default extra_class"
58+
end
59+
end
60+
end

0 commit comments

Comments
 (0)