diff --git a/playbook-website/Gemfile.lock b/playbook-website/Gemfile.lock index 676dadf1dd..cdd5597529 100644 --- a/playbook-website/Gemfile.lock +++ b/playbook-website/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../playbook specs: - playbook_ui (14.3.1) + playbook_ui (14.4.0) actionpack (>= 5.2.4.5) actionview (>= 5.2.4.5) activesupport (>= 5.2.4.5) diff --git a/playbook-website/app/controllers/pages_controller.rb b/playbook-website/app/controllers/pages_controller.rb index c5e34a3d8b..17e96004e2 100755 --- a/playbook-website/app/controllers/pages_controller.rb +++ b/playbook-website/app/controllers/pages_controller.rb @@ -3,6 +3,8 @@ require "will_paginate" require "playbook/pagination_renderer" require "will_paginate/array" +require "parser/current" +require "erb" require "ostruct" class PagesController < ApplicationController @@ -145,8 +147,99 @@ def kit_playground_rails render "pages/rails_in_react_playground", layout: "layouts/fullscreen" end + def parse_erb(code) + parsed = code.scan(/<%=?\s*(.*?)\s*%>/).flatten.join("\n") + Parser::CurrentRuby.parse(parsed) + end + + def detect_ruby_methods(root_node) + stack = [root_node] # Initialize the stack with the root node + methods = [] + until stack.empty? + node = stack.pop # Get the current node from the stack + + next unless node.is_a?(Parser::AST::Node) + + # Check if the current node is a `:send` node and has `:system` as the method name + methods.push(node.children[1]) if node.type == :send + # Push all child nodes onto the stack + stack.concat(node.children) + end + methods + end + + def white_list + %i[<< to_s pb_rails +@ freeze] + end + + # Define methods outside of the main method + def add_xstr_node(node, xstr_nodes) + xstr_nodes << node if node.type == :xstr + end + + def add_gvar_node(node, gvar_nodes) + gvar_nodes << node if node.type == :gvar + end + + def traverse_node_gvar(node, xstr_nodes) + case node + when Parser::AST::Node + add_gvar_node(node, xstr_nodes) + # Recur for each child node + node.children.each do |child| + traverse_node_gvar(child, xstr_nodes) + end + end + end + + def detect_gvar_nodes(ast_node) + gvar_nodes = [] + # Start traversing from the root node + traverse_node_gvar(ast_node, gvar_nodes) + gvar_nodes + end + + def traverse_node(node, xstr_nodes) + case node + when Parser::AST::Node + add_xstr_node(node, xstr_nodes) + # Recur for each child node + node.children.each do |child| + traverse_node(child, xstr_nodes) + end + end + end + + def detect_xstr_nodes(ast_node) + xstr_nodes = [] + # Start traversing from the root node + traverse_node(ast_node, xstr_nodes) + xstr_nodes + end + + def is_erb_safe?(code) + node = parse_erb(code) + detect_gvar_nodes(parse_erb(erb_code_params)).empty? && + detect_ruby_methods(node) - white_list == [] && + detect_xstr_nodes(parse_erb(erb_code_params)).empty? + end + + def unsafe_code + detect_ruby_methods(parse_erb(erb_code_params)).uniq - white_list + end + + def playground_response + if is_erb_safe?(erb_code_params) + erb_code_params + elsif unsafe_code.any? + "The code provided can't be run please remove these methods: #{unsafe_code.join(', ')}" + else + "The code provided can't be run. Only use pb_rails." + end + end + def rails_pg_render - render inline: erb_code_params + render inline: playground_response rescue => e render json: { error: e }, status: 400 end diff --git a/playbook-website/app/javascript/components/MainSidebar/MenuData/SidebarNavItems.ts b/playbook-website/app/javascript/components/MainSidebar/MenuData/SidebarNavItems.ts index 8a830ec6d7..400a23b847 100644 --- a/playbook-website/app/javascript/components/MainSidebar/MenuData/SidebarNavItems.ts +++ b/playbook-website/app/javascript/components/MainSidebar/MenuData/SidebarNavItems.ts @@ -42,11 +42,11 @@ export const SideBarNavItems = [ leftIcon:"vial" }, - // { - // name: "Playground", - // key: "top-nav-item-6", - // link: "/kit_playground_rails", - // children: false, - // leftIcon:"laptop-code" - // } + { + name: "Playground", + key: "top-nav-item-6", + link: "/kit_playground_rails", + children: false, + leftIcon:"laptop-code" + } ] diff --git a/playbook-website/config/routes.rb b/playbook-website/config/routes.rb index 90079ee6af..ac11cd61c9 100644 --- a/playbook-website/config/routes.rb +++ b/playbook-website/config/routes.rb @@ -37,6 +37,8 @@ get "kits/:name/sandpack", to: "pages#kit_show_new", as: "kit_show_new" get "kits/:name/rails_in_react", to: "pages#rails_in_react", as: "rails_in_react" get "kits/:name/rails_raw", to: "pages#rails_raw", as: "rails_raw" + get "kit_playground_rails", to: "pages#kit_playground_rails", as: "kit_playground_rails" + post "rails_pg_render", to: "pages#rails_pg_render", as: "rails_pg_render" # Docs get "guides/:parent", to: "guides#md_doc", as: "guides_parent" diff --git a/playbook-website/lib/markdown_helper.rb b/playbook-website/lib/markdown_helper.rb index 9026e140bd..f2dbc8c383 100755 --- a/playbook-website/lib/markdown_helper.rb +++ b/playbook-website/lib/markdown_helper.rb @@ -72,6 +72,9 @@ def self.cache_key(text) def render_code(text, language) formatter = Rouge::Formatters::HTML.new(scope: ".highlight") lexer = Rouge::Lexer.find(language) + + lexer = Rouge::Lexers::PlainText.new if lexer.nil? + formatter.format(lexer.lex(text)) end diff --git a/playbook-website/spec/helpers/markdown_helper_spec.rb b/playbook-website/spec/helpers/markdown_helper_spec.rb new file mode 100644 index 0000000000..320bcbf93f --- /dev/null +++ b/playbook-website/spec/helpers/markdown_helper_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# spec/helpers/markdown_helper_spec.rb +require "rails_helper" + +RSpec.describe PlaybookWebsite::Markdown::Helper do + describe "#render_code" do + let(:helper) { Class.new { include PlaybookWebsite::Markdown::Helper }.new } + let(:formatter) { instance_double(Rouge::Formatters::HTML) } + let(:ruby_lexer) { instance_double(Rouge::Lexers::Ruby) } + let(:plain_text_lexer) { instance_double(Rouge::Lexers::PlainText) } + + before do + allow(Rouge::Formatters::HTML).to receive(:new).and_return(formatter) + allow(formatter).to receive(:format).and_return("") + end + + it "formats code with the correct lexer when language is found" do + allow(Rouge::Lexer).to receive(:find).with("ruby").and_return(ruby_lexer) + allow(ruby_lexer).to receive(:lex).with("puts 'Hello, World!'").and_return("lexed_code") + + expect(formatter).to receive(:format).with("lexed_code") + result = helper.render_code("puts 'Hello, World!'", "ruby") + expect(result).to eq("") + end + + it "uses PlainText lexer when language is not found" do + allow(Rouge::Lexer).to receive(:find).with("unknown_language").and_return(nil) + allow(Rouge::Lexers::PlainText).to receive(:new).and_return(plain_text_lexer) + allow(plain_text_lexer).to receive(:lex).with("Some plain text").and_return("lexed_plain_text") + + expect(formatter).to receive(:format).with("lexed_plain_text") + result = helper.render_code("Some plain text", "unknown_language") + expect(result).to eq("") + end + + it "handles nil language parameter" do + allow(Rouge::Lexer).to receive(:find).with(nil).and_return(nil) + allow(Rouge::Lexers::PlainText).to receive(:new).and_return(plain_text_lexer) + allow(plain_text_lexer).to receive(:lex).with("Some text").and_return("lexed_text") + + expect(formatter).to receive(:format).with("lexed_text") + result = helper.render_code("Some text", nil) + expect(result).to eq("") + end + end +end diff --git a/playbook/CHANGELOG.md b/playbook/CHANGELOG.md index 8d6f7aaf5a..75c3509657 100644 --- a/playbook/CHANGELOG.md +++ b/playbook/CHANGELOG.md @@ -1,3 +1,71 @@ +# Spot Everyone at a Glance: Meet the New Multiple Users Component! +##### September 18, 2024 + +![14 4 0](https://github.com/user-attachments/assets/9e1a90e9-7a5c-4e43-8192-8fbe74c8de28) + +We've added a fresh option to our MultipleUsersStacked component with the new bubble variant! This update showcases full user avatars in a sleek, Apple-inspired grid layout, enhancing recognition and clarity. It's a versatile addition to your toolkit, offering a refined way to display multiple users! + +[14.4.0](https://github.com/powerhome/playbook/tree/14.4.0) full list of changes: + +**Kit Enhancements:** +- Bubble Variant to Multiple Users Stacked [\#3692](https://github.com/powerhome/playbook/pull/3692) ([kangaree](https://github.com/kangaree)) +- Form Pills: Add All Color Options [\#3636](https://github.com/powerhome/playbook/pull/3636) ([elisashapiro](https://github.com/elisashapiro)) +- Implementing Radio customChildren Prop [\#3685](https://github.com/powerhome/playbook/pull/3685) ([carloslimasd](https://github.com/carloslimasd)) + +**Fixed Bugs:** +- Fix Undefined Method Error in SamplesController#show [\#3675](https://github.com/powerhome/playbook/pull/3675) ([skduncan](https://github.com/skduncan)) +- Make Rails Icons fixed-width [\#3656](https://github.com/powerhome/playbook/pull/3656) ([skduncan](https://github.com/skduncan)) + +**Improvements:** +- Remove Bad UTF-8 Character [\#3679](https://github.com/powerhome/playbook/pull/3679) ([@dhhuynh2](https://github.com/dhhuynh2)) +- Playground Sanitization Security [\#3624](https://github.com/powerhome/playbook/pull/3624) +- Upgraded Popper and React-Popper [\#3681](https://github.com/powerhome/playbook/pull/3681) ([jasperfurniss](https://github.com/jasperfurniss)) + + +[Full Changelog](https://github.com/powerhome/playbook/compare/14.3.2...14.4.0) + + +# Star Ratings and Page-Turning Upgrades 💫 +##### September 6, 2024 + +![anouncement](https://github.com/user-attachments/assets/d10fdaa7-3961-4d16-9e65-ce9a282c0188) + +Our Star Rating kit now shines brighter with added interactivity and hover effects! ⭐ And don’t forget to flip through content with our Pagination kit, now available in React in addition to Rails. + +[14.3.2](https://github.com/powerhome/playbook/tree/14.3.2) full list of changes: + +**Kit Enhancements:** + +- Add all Target Options to the Hashtag and Home Address Street Kits [\#3650](https://github.com/powerhome/playbook/pull/3650) ([skduncan](https://github.com/skduncan)) +- Adding Default Value Prop to Rails Star Rating Kit [\#3629](https://github.com/powerhome/playbook/pull/3629) ([carloslimasd](https://github.com/carloslimasd)) +- Collapsible Optional Icon [\#3643](https://github.com/powerhome/playbook/pull/3643) ([elisashapiro](https://github.com/elisashapiro)) +- Add "loading" Variant to Circle Icon Button [\#3633](https://github.com/powerhome/playbook/pull/3633) ([kangaree](https://github.com/kangaree)) + +**Fixed Bugs:** + +- Return empty string for SVG with HTTP errors [\404s, timeouts] [\#3658](https://github.com/powerhome/playbook/pull/3658) ([kangaree](https://github.com/kangaree)) +- Redirect If Guide Doesn't Exist [\#3653](https://github.com/powerhome/playbook/pull/3653) ([kangaree](https://github.com/kangaree)) +- React pagination glitchiness [\#3647](https://github.com/powerhome/playbook/pull/3647) ([elisashapiro](https://github.com/elisashapiro)) +- Fix @import playbook.scss Errors - Fix Theming / Overriding SCSS Variables [\#3608](https://github.com/powerhome/playbook/pull/3608) ([kangaree](https://github.com/kangaree)) +- Timestamp Kit Rails: Elapsed Time Variant Issue [\#3606](https://github.com/powerhome/playbook/pull/3606) ([skduncan](https://github.com/skduncan)) +- Fix Github Action Bug that Breaks Build [\#3655](https://github.com/powerhome/playbook/pull/3655) ([jasperfurniss](https://github.com/jasperfurniss)) +- Testing Incrementing for Github Action RC [\#3651](https://github.com/powerhome/playbook/pull/3651) ([jasperfurniss](https://github.com/jasperfurniss)) +- Fix Missing Template Error in PagesController [\#3645](https://github.com/powerhome/playbook/pull/3645) ([skduncan](https://github.com/skduncan)) +- Fixing TextArea Duplicated Classname [\#3660](https://github.com/powerhome/playbook/pull/3660) ([carloslimasd](https://github.com/carloslimasd)) +- Fix Collapsible Glitchiness When State Changes Quickly [\#3664](https://github.com/powerhome/playbook/pull/3664) ([kangaree](https://github.com/kangaree)) + +**Improvements:** + +- Fixes RC Github Action Limit [\#3666](https://github.com/powerhome/playbook/pull/3666) ([jasperfurniss](https://github.com/jasperfurniss)) +- Fix Kebab Logic [\#3649](https://github.com/powerhome/playbook/pull/3649) ([markdoeswork](https://github.com/markdoeswork)) +- Dialog Disable Rails - Remove Duplicate Data [\#3646](https://github.com/powerhome/playbook/pull/3646) ([markdoeswork](https://github.com/markdoeswork)) +- RTE Previewer + Output Rails [\#3625](https://github.com/powerhome/playbook/pull/3625) ([elisashapiro](https://github.com/elisashapiro)) +- Implement Error Handling and Additions for Github Action [\#3652](https://github.com/powerhome/playbook/pull/3652) ([jasperfurniss](https://github.com/jasperfurniss)) + + +[Full Changelog](https://github.com/powerhome/playbook/compare/14.3.0...14.3.2) + + # ⭐ Improving Usability, Flexibility, and Accessibility ⭐ ##### August 31st, 2024 diff --git a/playbook/Gemfile.lock b/playbook/Gemfile.lock index 5998716466..da0898c00f 100644 --- a/playbook/Gemfile.lock +++ b/playbook/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - playbook_ui (14.3.1) + playbook_ui (14.4.0) actionpack (>= 5.2.4.5) actionview (>= 5.2.4.5) activesupport (>= 5.2.4.5) diff --git a/playbook/app/entrypoints/playbook-rails.js b/playbook/app/entrypoints/playbook-rails.js index fb611584b5..6c11e2a8a8 100644 --- a/playbook/app/entrypoints/playbook-rails.js +++ b/playbook/app/entrypoints/playbook-rails.js @@ -45,6 +45,9 @@ PbNav.start() import PbStarRating from 'kits/pb_star_rating' PbStarRating.start() +import PbRadio from 'kits/pb_radio' +PbRadio.start() + import 'flatpickr' // React-Rendered Rails Kits ===== diff --git a/playbook/app/pb_kits/playbook/pb_collapsible/_helper_functions.ts b/playbook/app/pb_kits/playbook/pb_collapsible/_helper_functions.ts index 2079ec752f..4a5ba4d093 100644 --- a/playbook/app/pb_kits/playbook/pb_collapsible/_helper_functions.ts +++ b/playbook/app/pb_kits/playbook/pb_collapsible/_helper_functions.ts @@ -6,6 +6,10 @@ export const showElement = (elem: HTMLElement) => { elem.style.overflow = "hidden" // Once the transition is complete, remove the inline max-height so the content can scale responsively window.setTimeout(() => { + // If a user toggles multiple times quickly in a row, 'is-visible' can be removed by hideElement's timeout + if (!elem.classList.contains('is-visible')) { + elem.classList.add('is-visible') + } elem.style.height = ''; elem.style.overflow = "visible" }, 300); diff --git a/playbook/app/pb_kits/playbook/pb_date_picker/sass_partials/_flatpickr_styles.scss b/playbook/app/pb_kits/playbook/pb_date_picker/sass_partials/_flatpickr_styles.scss index cc950dae5c..d51ea5092a 100644 --- a/playbook/app/pb_kits/playbook/pb_date_picker/sass_partials/_flatpickr_styles.scss +++ b/playbook/app/pb_kits/playbook/pb_date_picker/sass_partials/_flatpickr_styles.scss @@ -322,8 +322,7 @@ background: rgba(0,0,0,0.05); } .flatpickr-current-month .numInputWrapper { - width: 6ch; - width: 7ch\0; + width: 7ch; display: inline-block; } .flatpickr-current-month .numInputWrapper span.arrowUp:after { @@ -774,4 +773,4 @@ span.flatpickr-weekday { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } -} \ No newline at end of file +} diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.scss b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.scss index d7573f9922..87f00ef302 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +++ b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.scss @@ -7,10 +7,7 @@ $selector: ".pb_form_pill"; $pb_form_pill_height: 27px; -$form_pill_colors: ( - primary: map-get($status_color_text, "primary"), - neutral: map-get($status_color_text, "neutral"), -); +$form_pill_colors: map-merge($status_color_text, map-merge($data_colors, $product_colors)); [class^=pb_form_pill_kit] { @@ -36,6 +33,18 @@ $form_pill_colors: ( color: $text_lt_default; } } + @if ($color_name == "warning") { + background-color: mix($yellow, $card_light, 10%); + .pb_form_pill_icon { + color: $yellow; + } + } + @if ($color_name == "accessories") { + background-color: mix($product_8_background, $card_light, 10%); + .pb_form_pill_icon { + color: $product_8_background; + } + } transition: background-color 0.2s ease; box-shadow: none; @media (hover:hover) { @@ -45,12 +54,24 @@ $form_pill_colors: ( background-color: mix($neutral, $card_light, 20%); border: 1px solid $border_light; } + @if ($color_name == "warning") { + background-color: mix($yellow, $card_light, 20%); + } + @if ($color_name == "accessories") { + background-color: mix($product_8_background, $card_light, 20%); + } } &:active { background-color: mix($color_value, $card_light, 30%); @if ($color_name == "neutral") { background-color: mix($neutral, $card_light, 30%); } + @if ($color_name == "warning") { + background-color: mix($yellow, $card_light, 30%); + } + @if ($color_name == "accessories") { + background-color: mix($product_8_background, $card_light, 30%); + } } } #{$selector}_text { @@ -58,6 +79,9 @@ $form_pill_colors: ( @if ($color_name == "neutral") { color: $text_lt_default; } + @if ($color_name == "warning") { + color: $yellow; + } padding: 0 $space-xs; } #{$selector}_close { @@ -76,6 +100,23 @@ $form_pill_colors: ( background-color: mix($neutral, $card_light, 60%); } } + @if ($color_name == "warning") { + color: $yellow; + } + &:hover { + background-color: mix($color_value, $card_light, 40%); + @if ($color_name == "warning") { + background-color: mix($yellow, $card_light, 40%); + } + } + @if ($color_name == "accessories") { + color: $product_8_background; + } + &:hover { + @if ($color_name == "accessories") { + background-color: mix($product_8_background, $card_light, 40%); + } + } } #{$selector}_tag { color: $color_value; @@ -83,6 +124,9 @@ $form_pill_colors: ( @if ($color_name == "neutral") { color: $text_lt_default; } + @if ($color_name == "warning") { + color: $yellow; + } } } } @@ -132,25 +176,23 @@ $form_pill_colors: ( } &.dark { @each $color_name, $color_value in $form_pill_colors { - // In dark mode, the base patterns below are needed for the next tickets for success, warning, error, info. - // Primary and Neutral are exceptions to the general rule in the handoff &[class*=_#{$color_name}] { - // background-color: mix($color_value, $card_dark, 10%); - // .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { - // color: $color_name; - // } - // .pb_form_pill_close { - // color: $color_name; - // &:hover { - // background-color: mix($color_value, $card_dark, 40%); - // } - // } - // &:hover { - // background-color: mix($color_value, $card_dark, 20%); - // } - // &:active { - // background-color: mix($color_value, $card_dark, 30%); - // } + background-color: mix($color_value, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $color_name; + } + .pb_form_pill_close { + color: $color_name; + &:hover { + background-color: mix($color_value, $card_dark, 40%); + } + } + &:hover { + background-color: mix($color_value, $card_dark, 20%); + } + &:active { + background-color: mix($color_value, $card_dark, 30%); + } @if ($color_name == "neutral") { background-color: transparent; border: 1px solid $border_dark; @@ -185,12 +227,192 @@ $form_pill_colors: ( } } &:hover { - background-color: mix($active_dark, $card_dark, 20px); + background-color: mix($active_dark, $card_dark, 20%); } &:active { background-color: mix($active_dark, $card_dark, 30%); } } + @if ($color_name == "data_1") { + background-color: mix($active_dark, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: mix($active_dark, $card_light, 90%); + } + .pb_form_pill_close { + color: mix($active_dark, $card_light, 90%); + &:hover { + background-color: mix($active_dark, $card_dark, 40%); + } + } + &:hover { + background-color: mix($active_dark, $card_dark, 20%); + } + &:active { + background-color: mix($data_1, $card_dark, 30%); + } + } + @if ($color_name == "data_6") { + background-color: mix($data_6, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $text_dk_light; + } + .pb_form_pill_close { + color: $text_dk_light; + &:hover { + background-color: mix($text_dk_light, $card_dark, 40%); + } + } + &:hover { + background-color: mix($data_6, $card_dark, 20%); + } + &:active { + background-color: mix($data_6, $card_dark, 30%); + } + } + @if ($color_name == "windows") { + background-color: mix($product_1_highlight, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $product_1_highlight; + } + .pb_form_pill_close { + color: $product_1_highlight; + &:hover { + background-color: mix($product_1_highlight, $card_dark, 40%); + } + } + &:hover { + background-color: mix($product_1_highlight, $card_dark, 20%); + } + &:active { + background-color: mix($product_1_highlight, $card_dark, 30%); + } + } + @if ($color_name == "siding") { + background-color: mix($product_2_highlight, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $product_2_highlight; + } + .pb_form_pill_close { + color: $product_2_highlight; + &:hover { + background-color: mix($product_2_highlight, $card_dark, 40%); + } + } + &:hover { + background-color: mix($product_2_highlight, $card_dark, 20%); + } + &:active { + background-color: mix($product_2_highlight, $card_dark, 30%); + } + } + @if ($color_name == "roofing") { + background-color: mix($product_5_highlight, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $product_5_highlight; + } + .pb_form_pill_close { + color: $product_5_highlight; + &:hover { + background-color: mix($product_5_highlight, $card_dark, 40%); + } + } + &:hover { + background-color: mix($product_5_highlight, $card_dark, 20%); + } + &:active { + background-color: mix($product_5_highlight, $card_dark, 30%); + } + } + @if ($color_name == "doors") { + background-color: mix($product_3_highlight, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $product_3_highlight; + } + .pb_form_pill_close { + color: $product_3_highlight; + &:hover { + background-color: mix($product_3_highlight, $card_dark, 40%); + } + } + &:hover { + background-color: mix($product_3_highlight, $card_dark, 20%); + } + &:active { + background-color: mix($product_3_highlight, $card_dark, 30%); + } + } + @if ($color_name == "gutters") { + background-color: mix($product_6_highlight, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $product_6_highlight; + } + .pb_form_pill_close { + color: $product_6_highlight; + &:hover { + background-color: mix($product_6_highlight, $card_dark, 40%); + } + } + &:hover { + background-color: mix($product_6_highlight, $card_dark, 20%); + } + &:active { + background-color: mix($product_6_highlight, $card_dark, 30%); + } + } + @if ($color_name == "solar") { + background-color: mix($product_4_highlight, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $product_4_highlight; + } + .pb_form_pill_close { + color: $product_4_highlight; + &:hover { + background-color: mix($product_4_highlight, $card_dark, 40%); + } + } + &:hover { + background-color: mix($product_4_highlight, $card_dark, 20%); + } + &:active { + background-color: mix($product_4_highlight, $card_dark, 30%); + } + } + @if ($color_name == "insulation") { + background-color: mix($product_7_highlight, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $product_7_highlight; + } + .pb_form_pill_close { + color: $product_7_highlight; + &:hover { + background-color: mix($product_7_highlight, $card_dark, 40%); + } + } + &:hover { + background-color: mix($product_7_highlight, $card_dark, 20%); + } + &:active { + background-color: mix($product_7_highlight, $card_dark, 30%); + } + } + @if ($color_name == "accessories") { + background-color: mix($product_8_highlight, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag, .pb_form_pill_icon { + color: $text_dk_light; + } + .pb_form_pill_close { + color: $text_dk_light; + &:hover { + background-color: mix($product_8_highlight, $card_dark, 40%); + } + } + &:hover { + background-color: mix($product_8_highlight, $card_dark, 20%); + } + &:active { + background-color: mix($product_8_highlight, $card_dark, 30%); + } + } } } } diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx index da5e17b13e..6a03b734af 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +++ b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx @@ -17,7 +17,7 @@ type FormPillProps = { avatarUrl?: string, size?: string, textTransform?: 'none' | 'lowercase', - color?: "primary" | "neutral", + color?: "primary" | "neutral" | "success" | "warning" | "error" | "info" | "data_1" | "data_2" | "data_3" | "data_4" | "data_5" | "data_6" | "data_7" | "data_8" | "windows" | "siding" | "roofing" | "doors" | "gutters" | "solar" | "insulation" | "accessories", data?: {[key: string]: string}, tabIndex?: number, icon?: string, diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_colors.html.erb b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_colors.html.erb new file mode 100644 index 0000000000..6138a292d9 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_colors.html.erb @@ -0,0 +1,117 @@ +<%= pb_rails("title", props: { text: "Status Colors", tag: "h4", size: 4, margin_bottom: "sm" }) %> + +<%= pb_rails("form_pill", props: { + text: "Primary", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "neutral", + text: "Neutral", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "success", + text: "Success", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "warning", + text: "Warning", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "error", + text: "Error", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "info", + text: "Info", + tabindex: 0, +}) %> + +<%= pb_rails("title", props: { text: "Data Colors", tag: "h4", size: 4, margin_bottom: "sm", margin_top: "md" }) %> + +<%= pb_rails("form_pill", props: { + color: "data_1", + text: "Data 1", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "data_2", + text: "Data 2", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "data_3", + text: "Data 3", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "data_4", + text: "Data 4", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "data_5", + text: "Data 5", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "data_6", + text: "Data 6", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "data_7", + text: "Data 7", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "data_8", + text: "Data 8", + tabindex: 0, +}) %> + +<%= pb_rails("title", props: { text: "Product Colors", tag: "h4", size: 4, margin_bottom: "sm", margin_top: "md" }) %> + +<%= pb_rails("form_pill", props: { + color: "windows", + text: "Windows", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "siding", + text: "Siding", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "roofing", + text: "Roofing", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "doors", + text: "Doors", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "gutters", + text: "Gutters", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "solar", + text: "Solar", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "insulation", + text: "Insulation", + tabindex: 0, +}) %> +<%= pb_rails("form_pill", props: { + color: "accessories", + text: "Accessories", + tabindex: 0, +}) %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_colors.jsx b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_colors.jsx new file mode 100644 index 0000000000..6142fbdf7b --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_colors.jsx @@ -0,0 +1,227 @@ +import React from 'react' +import { FormPill, Title } from 'playbook-ui' + +const FormPillColors = (props) => { + return ( +
+ + <FormPill + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Primary" + {...props} + /> + <FormPill + color="neutral" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Neutral" + {...props} + /> + <FormPill + color="success" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Success" + {...props} + /> + <FormPill + color="warning" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Warning" + {...props} + /> + <FormPill + color="error" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Error" + {...props} + /> + <FormPill + color="info" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Info" + {...props} + /> + <Title + marginBottom="sm" + marginTop="md" + size={4} + text="Data Colors" + {...props} + /> + <FormPill + color="data_1" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Data 1" + {...props} + /> + <FormPill + color="data_2" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Data 2" + {...props} + /> + <FormPill + color="data_3" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Data 3" + {...props} + /> + <FormPill + color="data_4" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Data 4" + {...props} + /> + <FormPill + color="data_5" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Data 5" + {...props} + /> + <FormPill + color="data_6" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Data 6" + {...props} + /> + <FormPill + color="data_7" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Data 7" + {...props} + /> + <FormPill + color="data_8" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Data 8" + {...props} + /> + <Title + marginBottom="sm" + marginTop="md" + size={4} + text="Product Colors" + {...props} + /> + <FormPill + color="windows" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Windows" + {...props} + /> + <FormPill + color="siding" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Siding" + {...props} + /> + <FormPill + color="roofing" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Roofing" + {...props} + /> + <FormPill + color="doors" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Doors" + {...props} + /> + <FormPill + color="gutters" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Gutters" + {...props} + /> + <FormPill + color="solar" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Solar" + {...props} + /> + <FormPill + color="insulation" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Insulation" + {...props} + /> + <FormPill + color="accessories" + onClick={() => { + alert('Click!') + }} + tabIndex={0} + text="Accessories" + {...props} + /> + </div> + ) +} +export default FormPillColors diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_colors.md b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_colors.md new file mode 100644 index 0000000000..228d11b3f2 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_colors.md @@ -0,0 +1 @@ +The Status, Data, and Product colors highlighted above can be passed to the `color` prop. Primary is the default color. Form pills with a text tag, an avatar, or an icon with text tag can all receive the `color` prop. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/example.yml b/playbook/app/pb_kits/playbook/pb_form_pill/docs/example.yml index 1b0657fbaf..ee3ee0e6d9 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/example.yml @@ -6,6 +6,7 @@ examples: - form_pill_tag: Form Pill Tag - form_pill_example: Example - form_pill_icon: Form Pill Icon + - form_pill_colors: Form Pill Colors react: - form_pill_user: Form Pill User @@ -13,3 +14,4 @@ examples: - form_pill_tag: Form Pill Tag - form_pill_example: Example - form_pill_icon: Form Pill Icon + - form_pill_colors: Form Pill Colors diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/index.js b/playbook/app/pb_kits/playbook/pb_form_pill/docs/index.js index 71ee92e726..2858abcb90 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/index.js @@ -3,3 +3,4 @@ export { default as FormPillSize } from './_form_pill_size.jsx' export { default as FormPillTag } from './_form_pill_tag.jsx' export { default as FormPillExample } from './_form_pill_example.jsx' export { default as FormPillIcon } from './_form_pill_icon.jsx' +export { default as FormPillColors } from './_form_pill_colors.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.rb b/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.rb index 5395096cf7..807b4a77bb 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.rb +++ b/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.rb @@ -12,7 +12,7 @@ class FormPill < Playbook::KitBase values: %w[none lowercase], default: "none" prop :color, type: Playbook::Props::Enum, - values: %w[primary neutral], + values: %w[primary neutral success warning error info data_1 data_2 data_3 data_4 data_5 data_6 data_7 data_8 windows siding roofing doors gutters solar insulation accessories], default: "primary" prop :tabindex prop :icon diff --git a/playbook/app/pb_kits/playbook/pb_icon/icon.rb b/playbook/app/pb_kits/playbook/pb_icon/icon.rb index 79c4880760..f5f9d8810b 100644 --- a/playbook/app/pb_kits/playbook/pb_icon/icon.rb +++ b/playbook/app/pb_kits/playbook/pb_icon/icon.rb @@ -9,7 +9,7 @@ class Icon < Playbook::KitBase prop :border, type: Playbook::Props::Boolean, default: false prop :fixed_width, type: Playbook::Props::Boolean, - default: false + default: true prop :flip, type: Playbook::Props::Enum, values: ["horizontal", "vertical", "both", nil], default: nil diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx b/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx index 4f7c1033b8..57a68acab6 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx @@ -34,6 +34,7 @@ type MultiLevelSelectProps = { onSelect?: (prop: { [key: string]: any }) => void selectedIds?: string[] variant?: "multi" | "single" + pillColor?: "primary" | "neutral" | "success" | "warning" | "error" | "info" | "data_1" | "data_2" | "data_3" | "data_4" | "data_5" | "data_6" | "data_7" | "data_8" | "windows" | "siding" | "roofing" | "doors" | "gutters" | "solar" | "insulation" | "accessories", } & GlobalProps const MultiLevelSelect = (props: MultiLevelSelectProps) => { @@ -50,7 +51,8 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => { treeData, onSelect = () => null, selectedIds, - variant = "multi" + variant = "multi", + pillColor = "primary" } = props const ariaProps = buildAriaProps(aria) @@ -467,6 +469,7 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => { inputDisplay === "pills" ? returnedArray.map((item, index) => ( <FormPill + color={pillColor} key={index} onClick={(event: any) => handlePillClose(event, item)} text={item.label} @@ -479,6 +482,7 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => { inputDisplay === "pills" ? defaultReturn.map((item, index) => ( <FormPill + color={pillColor} key={index} onClick={(event: any) => handlePillClose(event, item)} text={item.label} diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.html.erb b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.html.erb new file mode 100644 index 0000000000..9be5731462 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.html.erb @@ -0,0 +1,72 @@ +<% treeData = [{ + label: "Power Home Remodeling", + value: "Power Home Remodeling", + id: "100", + expanded: true, + children: [ + { + label: "People", + value: "People", + id: "101", + expanded: true, + children: [ + { + label: "Talent Acquisition", + value: "Talent Acquisition", + id: "102", + }, + { + label: "Business Affairs", + value: "Business Affairs", + id: "103", + children: [ + { + label: "Initiatives", + value: "Initiatives", + id: "104", + }, + { + label: "Learning & Development", + value: "Learning & Development", + id: "105", + }, + ], + }, + { + label: "People Experience", + value: "People Experience", + id: "106", + }, + ], + }, + { + label: "Contact Center", + value: "Contact Center", + id: "107", + children: [ + { + label: "Appointment Management", + value: "Appointment Management", + id: "108", + }, + { + label: "Customer Service", + value: "Customer Service", + id: "109", + }, + { + label: "Energy", + value: "Energy", + id: "110", + }, + ], + }, + ], +}] %> + +<%= pb_rails("multi_level_select", props: { + id: "multi-level-select-default-rails", + name: "my_array", + tree_data: treeData, + pill_color: "neutral" +}) %> diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.jsx b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.jsx new file mode 100644 index 0000000000..cdca67ceb8 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.jsx @@ -0,0 +1,91 @@ +import React from "react"; +import MultiLevelSelect from "../_multi_level_select"; + +const treeData = [ + { + label: "Power Home Remodeling", + value: "Power Home Remodeling", + id: "powerhome1", + expanded: true, + children: [ + { + label: "People", + value: "People", + id: "people1", + expanded: true, + children: [ + { + label: "Talent Acquisition", + value: "Talent Acquisition", + id: "talent1", + }, + { + label: "Business Affairs", + value: "Business Affairs", + id: "business1", + children: [ + { + label: "Initiatives", + value: "Initiatives", + id: "initiative1", + }, + { + label: "Learning & Development", + value: "Learning & Development", + id: "development1", + }, + ], + }, + { + label: "People Experience", + value: "People Experience", + id: "experience1", + }, + ], + }, + { + label: "Contact Center", + value: "Contact Center", + id: "contact1", + children: [ + { + label: "Appointment Management", + value: "Appointment Management", + id: "appointment1", + }, + { + label: "Customer Service", + value: "Customer Service", + id: "customer1", + }, + { + label: "Energy", + value: "Energy", + id: "energy1", + }, + ], + }, + ], + }, +]; + +const MultiLevelSelectColor = (props) => { + return ( + <div> + <MultiLevelSelect + id='multiselect-color' + onSelect={(selectedNodes) => + console.log( + "Selected Items", + selectedNodes + ) + } + pillColor="neutral" + treeData={treeData} + {...props} + /> + </div> + ) +}; + +export default MultiLevelSelectColor; diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color_rails.md b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color_rails.md new file mode 100644 index 0000000000..08f54c6329 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color_rails.md @@ -0,0 +1 @@ +Change the form pill color by passing the optional `pill_color` prop. Product, Data, and Status colors are available options. Check them out <a href="https://playbook.powerapp.cloud/kits/form_pill#form-pill-colors" target="_blank">here</a> in the Form Pill colors example. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color_react.md b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color_react.md new file mode 100644 index 0000000000..ca5350c203 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color_react.md @@ -0,0 +1 @@ +Change the form pill color by passing the optional `pillColor` prop. Product, Data, and Status colors are available options. Check them out <a href="https://playbook.powerapp.cloud/kits/form_pill/react#form-pill-colors" target="_blank">here</a> in the Form Pill colors example. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml index a6c5b50dcf..c232dee812 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml @@ -6,6 +6,7 @@ examples: - multi_level_select_return_all_selected: Return All Selected - multi_level_select_selected_ids: Selected Ids - multi_level_select_with_form: With Form + - multi_level_select_color: With Pills (Custom Color) react: - multi_level_select_default: Default @@ -13,3 +14,4 @@ examples: - multi_level_select_single_children_only: Single Select w/ Hidden Radios - multi_level_select_return_all_selected: Return All Selected - multi_level_select_selected_ids_react: Selected Ids + - multi_level_select_color: With Pills (Custom Color) diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/index.js b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/index.js index 3b0bd7eef8..6b8be4ec17 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/index.js @@ -3,3 +3,4 @@ export { default as MultiLevelSelectSingle } from './_multi_level_select_single. export { default as MultiLevelSelectSingleChildrenOnly } from './_multi_level_select_single_children_only.jsx' export { default as MultiLevelSelectReturnAllSelected } from './_multi_level_select_return_all_selected.jsx' export { default as MultiLevelSelectSelectedIdsReact } from "./_multi_level_select_selected_ids_react.jsx" +export { default as MultiLevelSelectColor } from './_multi_level_select_color.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb b/playbook/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb index 3783394005..a7e1a53073 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb @@ -19,6 +19,9 @@ class MultiLevelSelect < Playbook::KitBase prop :variant, type: Playbook::Props::Enum, values: %w[multi single], default: "multi" + prop :pill_color, type: Playbook::Props::Enum, + values: %w[primary neutral success warning error info data_1 data_2 data_3 data_4 data_5 data_6 data_7 data_8 windows siding roofing doors gutters solar insulation accessories], + default: "primary" def classname generate_classname("pb_multi_level_select") @@ -34,6 +37,7 @@ def multi_level_select_options selectedIds: selected_ids, input_name: input_name, variant: variant, + pillColor: pill_color, } end end diff --git a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss index 8c9aeab33b..d1ed6e11ab 100644 --- a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss +++ b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss @@ -3,8 +3,25 @@ @import "../tokens/opacity"; @import "../pb_avatar/avatar"; +@mixin avatar-size($size) { + height: $size; + width: $size; + .avatar_wrapper { + width: $size; + height: $size; + } +} + +@mixin position($position) { + position: absolute; + @each $pos, $val in $position { + #{$pos}: $val; + } +} + [class^=pb_multiple_users_stacked_kit] { $container_size: map-get($avatar-sizes, "xs"); + $bubble_container_size: map-get($avatar-sizes, "sm"); $overlap: -15px; $border_size: 1px; $stacked_size: 18px; @@ -17,34 +34,24 @@ flex-shrink: 0; flex-grow: 0; [class^=pb_avatar_kit].pb_multiple_users_stacked_item { - height: $stacked_size; - width: $stacked_size; - &.dark { - .avatar_wrapper { - border: $pb_multiple_users_border_size solid $bg_dark; - } - } + @include avatar-size($stacked_size); + &.dark { .avatar_wrapper { - border: $border_size solid $white; - height: $stacked_size; - width: $stacked_size; - img { - z-index: 0; - } + border: $border_size solid $bg_dark; } } - &[class*=_single] .pb_multiple_users_stacked_item { - width: $container_size; - height: $container_size; .avatar_wrapper { - width: $container_size; - height: $container_size; + border: $border_size solid $white; + img { + z-index: 0; + } } } + &[class*=_single] .pb_multiple_users_stacked_item { + @include avatar-size($container_size); + } [class^=pb_avatar_kit].second_item, [class^=pb_badge_kit].second_item { - position: absolute; - bottom: 0; - right: 0; + @include position((bottom: 0, right: 0)); z-index: 2; background: tint($primary, 90%); border-radius: $border_rad_mega; @@ -63,4 +70,73 @@ font-size: 0; color: transparent; } + + &[class*=_bubble] { + @include avatar-size($bubble_container_size); + background-color: $bg_light; + border-radius: 50%; + + &.dark { + background-color: $card_dark; + } + + [class^=pb_avatar_kit].pb_multiple_users_stacked_item { + &.dark { + .avatar_wrapper { + border: $border_size solid $border_dark; + } + } + } + + [class^=pb_avatar_kit] { + &.first_item { + @include position((top: 4px, left: 3px)); + @include avatar-size(20px); + + &.triple_bubble { + @include position((top: 4px, left: 4px)); + @include avatar-size(16px); + } + + &.quadruple_bubble { + @include position((top: 5px, left: 3px)); + @include avatar-size(16px); + } + } + + &.second_item { + @include position((bottom: 5px, right: 4px)); + @include avatar-size(12px); + + &.triple_bubble { + @include position((top: 13px, right: 2px)); + } + + &.quadruple_bubble { + @include position((bottom: 9px, right: 4px)); + } + } + + &.third_item { + @include position((bottom: 3px, left: 11px)); + @include avatar-size(10px); + + &.quadruple_bubble { + @include position((bottom: 3px, left: 9px)); + } + } + + &.fourth_item { + @include position((top: 5px, right: 6px)); + @include avatar-size(8px); + } + } + } + + &[class*=_single_bubble] { + [class^=pb_avatar_kit].first_item { + @include position((top: 0, left: 0)); + @include avatar-size($bubble_container_size); + } + } } diff --git a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.test.js b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.test.js index 1187a26830..c347044725 100644 --- a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.test.js +++ b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.test.js @@ -58,4 +58,170 @@ test('should pass aria prop', () => { render(<MultipleUsersStackedDefault />) const kit = screen.getByTestId(testId) expect(kit).toHaveAttribute('aria-label', testId) +}) + +const MultipleUsersStackedSingleBubble = () => { + return ( + <MultipleUsersStacked + aria={{ label: testId }} + className={className} + data={{ testid: testId }} + users={[ + { + name: "user1", + imageUrl: "imageUser1", + imageAlt: "nameUser1", + } + ]} + variant="bubble" + /> + ) +} + +test('should have a single bubble', () => { + render(<MultipleUsersStackedSingleBubble />) + const kit = screen.getByTestId(testId) + expect(kit).toHaveClass("pb_multiple_users_stacked_kit_single_bubble") + + const firstItem = kit.querySelector('.first_item'); + expect(firstItem).toBeInTheDocument(); +}) + +const MultipleUsersStackedDoubleBubble = () => { + return ( + <MultipleUsersStacked + aria={{ label: testId }} + className={className} + data={{ testid: testId }} + users={[ + { + name: "user1", + imageUrl: "imageUser1", + imageAlt: "nameUser1", + }, + { + name: "user2", + imageUrl: "imageUser2", + imageAlt: "nameUser2", + }, + ]} + variant="bubble" + /> + ) +} + +test('should have a double bubble', () => { + render(<MultipleUsersStackedDoubleBubble />) + const kit = screen.getByTestId(testId) + expect(kit).toHaveClass("pb_multiple_users_stacked_kit_bubble") + + const firstItem = kit.querySelector('.first_item'); + expect(firstItem).toBeInTheDocument(); + + const secondItem = kit.querySelector('.second_item'); + expect(secondItem).toBeInTheDocument(); +}) + +const MultipleUsersStackedTripleBubble = () => { + return ( + <MultipleUsersStacked + aria={{ label: testId }} + className={className} + data={{ testid: testId }} + users={[ + { + name: "user1", + imageUrl: "imageUser1", + imageAlt: "nameUser1", + }, + { + name: "user2", + imageUrl: "imageUser2", + imageAlt: "nameUser2", + }, + { + name: "user3", + imageUrl: "imageUser3", + imageAlt: "nameUser3", + }, + ]} + variant="bubble" + /> + ) +} + +test('should have a triple bubble', () => { + render(<MultipleUsersStackedTripleBubble />) + const kit = screen.getByTestId(testId) + expect(kit).toHaveClass("pb_multiple_users_stacked_kit_bubble") + + const firstItem = kit.querySelector('.first_item'); + expect(firstItem).toBeInTheDocument(); + expect(firstItem).toHaveClass("triple_bubble") + + const secondItem = kit.querySelector('.second_item'); + expect(secondItem).toBeInTheDocument(); + expect(secondItem).toHaveClass("triple_bubble") + + const thirdItem = kit.querySelector('.third_item'); + expect(thirdItem).toBeInTheDocument(); +}) + +const MultipleUsersStackedQuadrupleBubble = () => { + return ( + <MultipleUsersStacked + aria={{ label: testId }} + className={className} + data={{ testid: testId }} + users={[ + { + name: "user1", + imageUrl: "imageUser1", + imageAlt: "nameUser1", + }, + { + name: "user2", + imageUrl: "imageUser2", + imageAlt: "nameUser2", + }, + { + name: "user3", + imageUrl: "imageUser3", + imageAlt: "nameUser3", + }, + { + name: "user4", + imageUrl: "imageUser4", + imageAlt: "nameUser4", + }, + { + name: "user5", + imageUrl: "imageUser5", + imageAlt: "nameUser5", + }, + ]} + variant="bubble" + /> + ) +} + +test('should have a quadruple bubble', () => { + render(<MultipleUsersStackedQuadrupleBubble />) + const kit = screen.getByTestId(testId) + expect(kit).toHaveClass("pb_multiple_users_stacked_kit_bubble") + + const firstItem = kit.querySelector('.first_item'); + expect(firstItem).toBeInTheDocument(); + expect(firstItem).toHaveClass("quadruple_bubble") + + const secondItem = kit.querySelector('.second_item'); + expect(secondItem).toBeInTheDocument(); + expect(secondItem).toHaveClass("quadruple_bubble") + + const thirdItem = kit.querySelector('.third_item'); + expect(thirdItem).toBeInTheDocument(); + expect(thirdItem).toHaveClass("quadruple_bubble") + + const fourthItem = kit.querySelector('.fourth_item'); + expect(fourthItem).toBeInTheDocument(); }) \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx index 24c54bd43f..1d71b974a5 100644 --- a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx +++ b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx @@ -15,6 +15,7 @@ type MultipleUsersStackedProps = { htmlOptions?: {[key: string]: string | number | boolean | (() => void)}, id?: string, users: Array<{ [key: string]: string }>, + variant: "default" | "bubble", } const MultipleUsersStacked = (props: MultipleUsersStackedProps) => { @@ -26,10 +27,14 @@ const MultipleUsersStacked = (props: MultipleUsersStackedProps) => { htmlOptions = {}, id, users, + variant = "default", } = props const moreThanTwo = users.length > 2 const onlyOne = users.length == 1 + const isBubble = variant === "bubble" + const tripleBubble = isBubble && users.length === 3 + const quadrupleBubble = isBubble && users.length > 3 const displayCount = () => { return moreThanTwo ? 1 : users.length } @@ -38,29 +43,61 @@ const MultipleUsersStacked = (props: MultipleUsersStackedProps) => { const htmlProps = buildHtmlProps(htmlOptions) const classes = classnames(buildCss( 'pb_multiple_users_stacked_kit', - { single: onlyOne }), globalProps(props), className) + { single: onlyOne, bubble: isBubble }), globalProps(props), className) const firstUser = () => { return users.slice(0, 1).map((userObject, index) => { return ( <Avatar {...userObject} - className="pb_multiple_users_stacked_item" + className={`pb_multiple_users_stacked_item first_item${tripleBubble ? " triple_bubble" : ""}${quadrupleBubble ? " quadruple_bubble" : ""}`} dark={dark} key={index} - size="xs" + size={isBubble ? "sm" : "xs"} /> ) }) } const secondUser = () => { - if (moreThanTwo === false) { + if (!moreThanTwo || (isBubble && users.length > 1)) { return users.slice(1, 2).map((userObject, index) => { return ( <Avatar {...userObject} - className="pb_multiple_users_stacked_item second_item" + className={`pb_multiple_users_stacked_item second_item${tripleBubble ? " triple_bubble" : ""}${quadrupleBubble ? " quadruple_bubble" : ""}`} + dark={dark} + key={index} + size="xs" + /> + ) + }) + } + } + + const thirdUser = () => { + if (isBubble && users.length > 2) { + return users.slice(2, 3).map((userObject, index) => { + return ( + <Avatar + {...userObject} + className={`pb_multiple_users_stacked_item third_item${quadrupleBubble ? " quadruple_bubble" : ""}`} + dark={dark} + key={index} + size="xs" + /> + ) + }) + } + } + + const fourthUser = () => { + if (isBubble && users.length > 3) { + return users.slice(3, 4).map((userObject, index) => { + return ( + <Avatar + {...userObject} + className="pb_multiple_users_stacked_item fourth_item" dark={dark} key={index} size="xs" @@ -71,7 +108,7 @@ const MultipleUsersStacked = (props: MultipleUsersStackedProps) => { } const plusUsers = () => { - if (moreThanTwo === true) { + if (moreThanTwo && !isBubble) { return ( <Badge className="pb_multiple_users_stacked_item second_item" @@ -94,6 +131,8 @@ const MultipleUsersStacked = (props: MultipleUsersStackedProps) => { > {firstUser()} {secondUser()} + {thirdUser()} + {fourthUser()} {plusUsers()} </div> ) diff --git a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_bubble.html.erb b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_bubble.html.erb new file mode 100644 index 0000000000..9de076392f --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_bubble.html.erb @@ -0,0 +1,73 @@ +<%= pb_rails("multiple_users_stacked", props: { + variant: "bubble", + users: [ + { + name: "Patrick Welch", + image_url: "https://randomuser.me/api/portraits/men/9.jpg", + } + ] +}) %> + +<br/><br/> + +<%= pb_rails("multiple_users_stacked", props: { + variant: "bubble", + users: [ + { + name: "Patrick Welch", + image_url: "https://randomuser.me/api/portraits/men/9.jpg", + }, + { + name: "Lucille Sanchez", + image_url: "https://randomuser.me/api/portraits/women/6.jpg", + } + ] +}) %> + +<br/><br/> + +<%= pb_rails("multiple_users_stacked", props: { + variant: "bubble", + users: [ + { + name: "Patrick Welch", + image_url: "https://randomuser.me/api/portraits/men/9.jpg", + }, + { + name: "Lucille Sanchez", + image_url: "https://randomuser.me/api/portraits/women/6.jpg", + }, + { + name: "Beverly Reyes", + image_url: "https://randomuser.me/api/portraits/women/74.jpg", + }, + ] +}) %> + +<br/><br/> + +<%= pb_rails("multiple_users_stacked", props: { + variant: "bubble", + users: [ + { + name: "Patrick Welch", + image_url: "https://randomuser.me/api/portraits/men/9.jpg", + }, + { + name: "Lucille Sanchez", + image_url: "https://randomuser.me/api/portraits/women/6.jpg", + }, + { + name: "Beverly Reyes", + image_url: "https://randomuser.me/api/portraits/women/74.jpg", + }, + { + name: "Keith Craig", + image_url: "https://randomuser.me/api/portraits/men/40.jpg", + }, + { + name: "Alicia Cooper", + image_url: "https://randomuser.me/api/portraits/women/46.jpg", + } + ] +}) %> diff --git a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_bubble.jsx b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_bubble.jsx new file mode 100644 index 0000000000..061c25a8f6 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_bubble.jsx @@ -0,0 +1,86 @@ +import React from 'react' + +import MultipleUsersStacked from '../_multiple_users_stacked' + +const MultipleUsersStackedBubble = (props) => { + return ( + <div> + <MultipleUsersStacked + users={[ + { + name: 'Patrick Welch', + imageUrl: 'https://randomuser.me/api/portraits/men/9.jpg', + }, + ]} + variant="bubble" + {...props} + /> + <br /> + <br /> + <MultipleUsersStacked + users={[ + { + name: 'Patrick Welch', + imageUrl: 'https://randomuser.me/api/portraits/men/9.jpg', + }, + { + name: 'Lucille Sanchez', + imageUrl: 'https://randomuser.me/api/portraits/women/6.jpg', + }, + ]} + variant="bubble" + {...props} + /> + <br /> + <br /> + <MultipleUsersStacked + users={[ + { + name: 'Patrick Welch', + imageUrl: 'https://randomuser.me/api/portraits/men/9.jpg', + }, + { + name: 'Lucille Sanchez', + imageUrl: 'https://randomuser.me/api/portraits/women/6.jpg', + }, + { + name: 'Beverly Reyes', + imageUrl: 'https://randomuser.me/api/portraits/women/74.jpg', + }, + ]} + variant="bubble" + {...props} + /> + <br /> + <br /> + <MultipleUsersStacked + users={[ + { + name: 'Patrick Welch', + imageUrl: 'https://randomuser.me/api/portraits/men/9.jpg', + }, + { + name: 'Lucille Sanchez', + imageUrl: 'https://randomuser.me/api/portraits/women/6.jpg', + }, + { + name: 'Beverly Reyes', + imageUrl: 'https://randomuser.me/api/portraits/women/74.jpg', + }, + { + name: 'Keith Craig', + imageUrl: 'https://randomuser.me/api/portraits/men/40.jpg', + }, + { + name: 'Alicia Cooper', + imageUrl: 'https://randomuser.me/api/portraits/women/46.jpg', + }, + ]} + variant="bubble" + {...props} + /> + </div> + ) +} + +export default MultipleUsersStackedBubble diff --git a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/example.yml b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/example.yml index ddb9f97761..fde4f3211b 100644 --- a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/example.yml @@ -2,10 +2,12 @@ examples: rails: - multiple_users_stacked_default: Default + - multiple_users_stacked_bubble: Bubble react: - multiple_users_stacked_default: Default + - multiple_users_stacked_bubble: Bubble swift: - multiple_users_stacked_default_swift: Default diff --git a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/index.js b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/index.js index 392320a016..ec6c88a5e4 100644 --- a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/docs/index.js @@ -1 +1,2 @@ export { default as MultipleUsersStackedDefault } from './_multiple_users_stacked_default.jsx' +export { default as MultipleUsersStackedBubble } from './_multiple_users_stacked_bubble.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb index 6cf6be3661..1b004c88e9 100644 --- a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb +++ b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb @@ -1,14 +1,18 @@ <%= pb_content_tag do %> - <%= pb_rails("avatar", props: object.users[0].merge({size: "xs", classname: "pb_multiple_users_stacked_item", dark: object.dark}) ) %> + <%= pb_rails("avatar", props: object.users[0].merge({size: object.bubble ? "sm" : "xs", classname: "pb_multiple_users_stacked_item first_item#{object.triple_bubble ? " triple_bubble" : ""}#{object.quadruple_bubble ? " quadruple_bubble" : ""}", dark: object.dark}) ) %> <% unless object.only_one %> - <% if object.more_than_two %> + <% if object.more_than_two && !object.bubble %> <%= pb_rails("badge", props: { dark: object.dark, text: "+#{object.users.count - object.display_count}", variant: "primary", rounded: true, classname: "pb_multiple_users_stacked_item second_item" }) %> + <% elsif object.bubble %> + <% object.users.slice(1,3).each_with_index do |item, idx| %> + <%= pb_rails("avatar", props: item.merge({size: "xs", classname: "pb_multiple_users_stacked_item #{idx == 0 ? "second_item#{object.triple_bubble ? " triple_bubble" : ""}#{object.quadruple_bubble ? " quadruple_bubble" : ""}" : idx == 1 ? "third_item#{object.quadruple_bubble ? " quadruple_bubble" : ""}" : "fourth_item"}", dark: object.dark}) ) %> + <% end %> <% else %> <%= pb_rails("avatar", props: object.users[1].merge({size: "xs", classname: "pb_multiple_users_stacked_item second_item", dark: object.dark}) ) %> <% end %> diff --git a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.rb b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.rb index 2d6e031500..cc1be5dbf6 100644 --- a/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.rb +++ b/playbook/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.rb @@ -5,6 +5,10 @@ module PbMultipleUsersStacked class MultipleUsersStacked < Playbook::KitBase prop :users, type: Playbook::Props::HashArray, required: true + prop :variant, type: Playbook::Props::Enum, + values: %w[default bubble], + default: "default" + def more_than_two users.count > 2 end @@ -17,8 +21,20 @@ def display_count more_than_two ? 1 : users.count end + def bubble + variant == "bubble" + end + + def triple_bubble + bubble && users.count === 3 + end + + def quadruple_bubble + bubble && users.count > 3 + end + def classname - generate_classname("pb_multiple_users_stacked_kit", single_class) + generate_classname("pb_multiple_users_stacked_kit", single_class, bubble_class) end private @@ -26,6 +42,10 @@ def classname def single_class only_one ? "single" : nil end + + def bubble_class + bubble ? "bubble" : nil + end end end end diff --git a/playbook/app/pb_kits/playbook/pb_radio/_radio.tsx b/playbook/app/pb_kits/playbook/pb_radio/_radio.tsx index a1436f8135..1868dee9da 100644 --- a/playbook/app/pb_kits/playbook/pb_radio/_radio.tsx +++ b/playbook/app/pb_kits/playbook/pb_radio/_radio.tsx @@ -1,27 +1,28 @@ -import React, { forwardRef, isValidElement, useRef } from 'react' +/*eslint-disable react/no-multi-comp, flowtype/space-before-type-colon */ + +import React, { forwardRef } from 'react' import Body from '../pb_body/_body' -import Flex from '../pb_flex/_flex' import classnames from 'classnames' import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props' import { globalProps, GlobalProps } from '../utilities/globalProps' type RadioProps = { - aria?: { [key: string]: string }, + aria?: {[key: string]: string}, alignment?: string, checked?: boolean, children?: React.ReactChild[] | React.ReactChild, className?: string, dark?: boolean, - data?: { [key: string]: string }, + data?: {[key: string]: string}, disabled?: boolean, error?: boolean, - htmlOptions?: { [key: string]: string | number | boolean | (() => void) }, + htmlOptions?: {[key: string]: string | number | boolean | (() => void)}, id?: string, label: string, name?: string, value?: string, text?: string, - onChange: (event: React.FormEvent<HTMLInputElement> | null) => void, + onChange: (event: React.FormEvent<HTMLInputElement> | null)=>void, } & GlobalProps const Radio = ({ @@ -30,9 +31,9 @@ const Radio = ({ children, className, dark = false, + data = {}, disabled = false, error = false, - data = {}, htmlOptions = {}, id, label, @@ -41,103 +42,52 @@ const Radio = ({ value = 'radio_text', onChange = () => { void 0 }, ...props -}: RadioProps ) => { - const radioRef = useRef(null); - - const ariaProps = buildAriaProps(aria); - const dataProps = buildDataProps(data); - const htmlProps = buildHtmlProps(htmlOptions); +}: RadioProps, ref: any) => { + const ariaProps = buildAriaProps(aria) + const dataProps = buildDataProps(data) + const htmlProps = buildHtmlProps(htmlOptions) const classes = classnames( - buildCss('pb_radio_kit', alignment), - dark ? 'dark' : null, - error ? 'error' : null, + buildCss('pb_radio_kit', alignment ), + dark ? 'dark': null, error ? 'error': null, globalProps(props), - className - ); - - const classesCustom = classnames( - dark ? 'dark' : null, - error ? 'error' : null, - globalProps(props), - className - ); - - const isCustomChild = children && isValidElement(children) && children.type !== 'input'; + className) const displayRadio = (props: RadioProps & any) => { - if (isValidElement(children) && children.type === 'input') { - return children; - } else if (isCustomChild || !children) { - return ( - <input - disabled={disabled} - id={id} - name={name} - onChange={onChange} - ref={radioRef} - text={text} - type="radio" - value={value} - {...props} - /> - ); - } - }; - - const handleContainerClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent> | undefined) => { - if (event) { - const target = event.target as HTMLElement; - if ( - target.id === 'pb-radio-children-wrapper' || - target.closest('#pb-radio-children-wrapper') - ) { - radioRef.current?.click(); - } - } - }; + if (children) + return (children) + else + return ( + <input + disabled={disabled} + id={id} + name={name} + onChange={onChange} + ref={ref} + text={text} + type="radio" + value={value} + {...props} + /> + )} return ( - isCustomChild ? ( - <Flex - {...ariaProps} - {...dataProps} - {...htmlProps} - align='center' - className={classesCustom} - cursor='pointer' - htmlFor={id} - htmlOptions={{ - onClick: ((event: React.MouseEvent<HTMLDivElement, MouseEvent>) => { - handleContainerClick(event); - }) as unknown as () => void - }} - id="radio-container" - > - <label className={buildCss('pb_radio_kit', alignment)}> - <>{displayRadio(props)}</> - <span className="pb_radio_button" /> - </label> - <div id="pb-radio-children-wrapper"> {children} </div> - </Flex> - ) : ( - <label - {...ariaProps} - {...dataProps} - {...htmlProps} - className={classes} - htmlFor={id} - > - <>{displayRadio(props)}</> - <span className="pb_radio_button" /> - <Body - dark={dark} - status={error ? 'negative' : null} - text={label} - variant={null} - /> - </label> - ) - ); -}; + <label + {...ariaProps} + {...dataProps} + {...htmlProps} + className={classes} + htmlFor={id} + > + <>{displayRadio(props)}</> + <span className="pb_radio_button" /> + <Body + dark={dark} + status={error ? 'negative' : null} + text={label} + variant={null} + /> + </label> + ) +} -export default forwardRef(Radio); +export default forwardRef(Radio) diff --git a/playbook/app/pb_kits/playbook/pb_radio/docs/_radio_children.jsx b/playbook/app/pb_kits/playbook/pb_radio/docs/_radio_children.jsx deleted file mode 100644 index 70f42d7c84..0000000000 --- a/playbook/app/pb_kits/playbook/pb_radio/docs/_radio_children.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react' -import Radio from '../_radio' -import Select from '../../pb_select/_select' -import Typeahead from '../../pb_typeahead/_typeahead' -import Title from '../../pb_title/_title' - -const RadioChildren = (props) => { - - - const options = [ - { label: 'Orange', value: 'Orange' }, - { label: 'Red', value: 'Red' }, - { label: 'Green', value: 'Green' }, - { label: 'Blue', value: 'Blue' }, - ] - - return ( - <div> - <Radio - label="Select" - name="Group1" - tabIndex={0} - value="Select" - {...props} - > - <Select - minWidth="xs" - options={options} - /> - </Radio> - <Radio - label="Typeahead" - name="Group1" - tabIndex={0} - value="Typeahead" - {...props} - > - <Typeahead - minWidth="xs" - options={options} - /> - </Radio> - <br /> - <Radio - defaultChecked={false} - label="Typography" - name="Group1" - value="Typography" - {...props} - > - <Title text="Custom Typography" /> - </Radio> - </div> - ) -} -export default RadioChildren diff --git a/playbook/app/pb_kits/playbook/pb_radio/docs/_radio_custom_children.html.erb b/playbook/app/pb_kits/playbook/pb_radio/docs/_radio_custom_children.html.erb new file mode 100644 index 0000000000..f5593332a6 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_radio/docs/_radio_custom_children.html.erb @@ -0,0 +1,48 @@ +<% + options = [ + { label: "Orange", value: "Orange" }, + { label: "Red", value: "Red" }, + { label: "Green", value: "Green" }, + { label: "Blue", value: "Blue" }, + ] +%> + +<%= pb_rails("radio", props: { + custom_children: true, + label: "Select", + name: "Group1", + value: "Select", +}) do %> + <%= pb_rails("select", props: { + min_width: "xs", + options: options, + }) %> +<% end %> + +<%= pb_rails("radio", props: { + custom_children: true, + label: "Typeahead", + name: "Group1", + value: "Typeahead", +}) do %> + <%= pb_rails("typeahead", props: { + id: "typeahead-radio", + is_multi: false, + min_width: "xs", + options: options, + placeholder: "Select...", + }) + %> +<% end %> + +<%= pb_rails("radio", props: { + custom_children: true, + label: "Typography", + name: "Group1", + value: "Typography", +}) do %> + <%= pb_rails("title", props: { + text: "Custom Typography", + }) + %> +<% end %> diff --git a/playbook/app/pb_kits/playbook/pb_radio/docs/_radio_custom_children.md b/playbook/app/pb_kits/playbook/pb_radio/docs/_radio_custom_children.md new file mode 100644 index 0000000000..ca118ecab1 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_radio/docs/_radio_custom_children.md @@ -0,0 +1 @@ +Use the `custom_children` prop to enable the use of kits instead of text labels. diff --git a/playbook/app/pb_kits/playbook/pb_radio/docs/example.yml b/playbook/app/pb_kits/playbook/pb_radio/docs/example.yml index 6c3afd5d70..0d8fb7d8f3 100644 --- a/playbook/app/pb_kits/playbook/pb_radio/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_radio/docs/example.yml @@ -7,6 +7,7 @@ examples: - radio_options: With Options - radio_alignment: Alignment - radio_disabled: Disabled + - radio_custom_children: Custom Children react: - radio_default: Default @@ -14,7 +15,6 @@ examples: - radio_error: With Error - radio_alignment: Alignment - radio_disabled: Disabled - - radio_children: Children swift: - radio_default_swift: Default diff --git a/playbook/app/pb_kits/playbook/pb_radio/docs/index.js b/playbook/app/pb_kits/playbook/pb_radio/docs/index.js index 1d9a1ede93..75c68c8e49 100644 --- a/playbook/app/pb_kits/playbook/pb_radio/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_radio/docs/index.js @@ -3,4 +3,3 @@ export { default as RadioCustom } from './_radio_custom.jsx' export { default as RadioError } from './_radio_error.jsx' export { default as RadioAlignment } from './_radio_alignment.jsx' export { default as RadioDisabled } from './_radio_disabled.jsx' -export { default as RadioChildren } from './_radio_children.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_radio/index.js b/playbook/app/pb_kits/playbook/pb_radio/index.js new file mode 100644 index 0000000000..949c5fddd8 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_radio/index.js @@ -0,0 +1,17 @@ +import PbEnhancedElement from "../pb_enhanced_element" + +const RADIO_SELECTOR = "[data-pb-radio-children]" +const RADIO_WRAPPER_SELECTOR = "[data-pb-radio-children-wrapper]" + +export default class PbRadio extends PbEnhancedElement { + static get selector() { + return RADIO_SELECTOR + } + + connect() { + const radioWrapperElement = this.element.parentElement.querySelector(RADIO_WRAPPER_SELECTOR) + radioWrapperElement.addEventListener("click", () => { + this.element.querySelector("input[type='radio']").click() + }) + } +} diff --git a/playbook/app/pb_kits/playbook/pb_radio/radio.html.erb b/playbook/app/pb_kits/playbook/pb_radio/radio.html.erb index c7d2b70e99..ac28097d9d 100644 --- a/playbook/app/pb_kits/playbook/pb_radio/radio.html.erb +++ b/playbook/app/pb_kits/playbook/pb_radio/radio.html.erb @@ -1,18 +1,40 @@ -<%= content_tag(:label, +<% if object.custom_children %> + <%= pb_rails("flex", props: { aria: object.aria, - checked: object.checked, + align: "center", class: object.classname, + cursor: "pointer", data: object.data, - id: object.id, - value: object.value, - **combined_html_options) do %> - - <% if content.present? %> - <%= content %> - <% else %> - <%= radio_button_tag object.name, object.value, object.selected, object.input_options %> + **combined_html_options + }) do %> + <%= content_tag(:label, + 'data-pb-radio-children': 'true', + checked: object.checked, + class: object.classname, + id: object.id, + value: object.value) do %> + <%= input %> + <span class="pb_radio_button"></span> <% end %> + <div data-pb-radio-children-wrapper="true"> <%= content %> </div> + <% end %> +<% else %> + <%= content_tag(:label, + aria: object.aria, + checked: object.checked, + class: object.classname, + data: object.data, + id: object.id, + value: object.value, + **combined_html_options) do %> + + <% if content.present? %> + <%= content %> + <% else %> + <%= radio_button_tag object.name, object.value, object.selected, object.input_options %> + <% end %> - <span class="pb_radio_button"></span> - <%= pb_rails("body", props: { status: object.body_status, text: object.text, dark: object.dark }) %> -<% end %> + <span class="pb_radio_button"></span> + <%= pb_rails("body", props: { status: object.body_status, text: object.text, dark: object.dark }) %> + <% end %> +<% end %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_radio/radio.rb b/playbook/app/pb_kits/playbook/pb_radio/radio.rb index 2ac7a0148f..b3523274d3 100644 --- a/playbook/app/pb_kits/playbook/pb_radio/radio.rb +++ b/playbook/app/pb_kits/playbook/pb_radio/radio.rb @@ -20,6 +20,8 @@ class Radio < Playbook::KitBase default: "Radio Text" prop :value, type: Playbook::Props::String, default: "radio_text" + prop :custom_children, type: Playbook::Props::Boolean, + default: false def classname generate_classname("pb_radio_kit") + error_class + alignment_class @@ -34,7 +36,7 @@ def body_status end def input - radio_button_tag(name, value, checked, input_options.merge(disabled: disabled)) + radio_button_tag(name, value, checked, input_options.merge(disabled: disabled || input_options[:disabled])) end private diff --git a/playbook/app/pb_kits/playbook/pb_textarea/_textarea.tsx b/playbook/app/pb_kits/playbook/pb_textarea/_textarea.tsx index 276c6f6290..cc9997c936 100644 --- a/playbook/app/pb_kits/playbook/pb_textarea/_textarea.tsx +++ b/playbook/app/pb_kits/playbook/pb_textarea/_textarea.tsx @@ -92,7 +92,6 @@ const Textarea = ({ <Caption text={label} /> {children || ( <textarea - className="pb_textarea_kit" disabled={disabled} name={name} onChange={onChange} diff --git a/playbook/app/pb_kits/playbook/pb_textarea/textarea.html.erb b/playbook/app/pb_kits/playbook/pb_textarea/textarea.html.erb index 3974825f2a..3b757a4ab5 100644 --- a/playbook/app/pb_kits/playbook/pb_textarea/textarea.html.erb +++ b/playbook/app/pb_kits/playbook/pb_textarea/textarea.html.erb @@ -15,7 +15,6 @@ <%= text_area( :object, :method, - :class => "#{object.classname}", :max_characters => object.max_characters, :name => object.name, :onkeyup => object.onkeyup, diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx b/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx index 3bc76aac57..909a621a03 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx @@ -121,4 +121,19 @@ test('typeahead textinput has mb_sm class by default', () => { expect(kit).toHaveClass("pb_typeahead_kit mb_sm") const textInput = kit.querySelector(".pb_text_input_kit") expect(textInput).toHaveClass("mb_none") + +test('typeahead with colored pills', () => { + render( + <Typeahead + data={{ testid: 'pills-color-test' }} + defaultValue={[options[0]]} + isMulti + options={options} + pillColor="neutral" + /> + ) + + const kit = screen.getByTestId('pills-color-test') + const pill = kit.querySelector(".pb_form_pill_kit_neutral") + expect(pill).toBeInTheDocument() }) \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx index c7912b1dce..f4824fa572 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx @@ -46,12 +46,14 @@ type TypeaheadProps = { getOptionValue?: string | (() => any), name?: string, marginBottom?: "none" | "xxs" | "xs" | "sm" | "md" | "lg" | "xl", + pillColor?: "primary" | "neutral" | "success" | "warning" | "error" | "info" | "data_1" | "data_2" | "data_3" | "data_4" | "data_5" | "data_6" | "data_7" | "data_8" | "windows" | "siding" | "roofing" | "doors" | "gutters" | "solar" | "insulation" | "accessories", } & GlobalProps export type SelectValueType = { label: string, value: string, imageUrl?: string, + pillColor?: string, } type TagOnChangeValues = { @@ -78,6 +80,7 @@ const Typeahead = ({ id, loadOptions = noop, marginBottom = "sm", + pillColor, ...props }: TypeaheadProps) => { const selectProps = { @@ -107,6 +110,7 @@ const Typeahead = ({ onCreateOption: null as null, plusIcon: false, onMultiValueClick: (_option: SelectValueType): any => undefined, + pillColor: pillColor, ...props, } diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx index e3ce62ef5f..cef60a167e 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx @@ -8,6 +8,7 @@ import { SelectValueType } from '../_typeahead' type Props = { data: SelectValueType, multiValueTemplate: any, + pillColor?: "primary" | "neutral" | "success" | "warning" | "error" | "info" | "data_1" | "data_2" | "data_3" | "data_4" | "data_5" | "data_6" | "data_7" | "data_8" | "windows" | "siding" | "roofing" | "doors" | "gutters" | "solar" | "insulation" | "accessories", removeProps: any, selectProps: any, } @@ -15,48 +16,55 @@ type Props = { const MultiValue = (props: Props) => { const { removeProps } = props const { imageUrl, label } = props.data - const { multiKit } = props.selectProps + const { dark, multiKit, pillColor } = props.selectProps const formPillProps = { marginRight: 'xs', name: label, avatarUrl: '', + dark, } if (typeof imageUrl === 'string') formPillProps.avatarUrl = imageUrl return ( <components.MultiValueContainer - className="text_input_multivalue_container" - {...props} + className="text_input_multivalue_container" + {...props} > {multiKit === 'badge' && <Badge - closeProps={removeProps} - removeIcon - text={label} - variant="primary" + closeProps={removeProps} + removeIcon + text={label} + variant="primary" /> } {multiKit !== 'badge' && imageUrl && <FormPill - avatarUrl={imageUrl} - closeProps={removeProps} - marginRight="xs" - name={label} - size={multiKit === 'smallPill' ? 'small' : ''} - text='' + avatarUrl={imageUrl} + closeProps={removeProps} + color={pillColor} + dark={dark} + marginRight="xs" + name={label} + size={multiKit === 'smallPill' ? 'small' : ''} + text='' + {...props} /> } {multiKit !== 'badge' && !imageUrl && <FormPill - closeProps={removeProps} - marginRight="xs" - name='' - size={multiKit === 'smallPill' ? 'small' : ''} - text={label} + closeProps={removeProps} + color={pillColor} + dark={dark} + marginRight="xs" + name='' + size={multiKit === 'smallPill' ? 'small' : ''} + text={label} + {...props} /> } </components.MultiValueContainer> diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color.html.erb b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color.html.erb new file mode 100644 index 0000000000..98f10b3742 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color.html.erb @@ -0,0 +1,31 @@ +<% + options = [ + { label: 'Windows', value: '#FFA500' }, + { label: 'Siding', value: '#FF0000' }, + { label: 'Doors', value: '#00FF00' }, + { label: 'Roofs', value: '#0000FF' }, + ] +%> + +<%= pb_rails("typeahead", props: { id: "typeahead-pills-example1", pill_color: "neutral", default_options: [options.first], options: options, label: "Colors", name: :foo, pills: true }) %> + +<%= pb_rails("button", props: {id: "clear-pills", text: "Clear All Options", variant: "secondary"}) %> + +<!-- This section is an example of the available JavaScript event hooks --> +<%= javascript_tag defer: "defer" do %> + document.addEventListener("pb-typeahead-kit-typeahead-pills-example1-result-option-select", function(event) { + console.log('Option selected') + console.dir(event.detail) + }) + document.addEventListener("pb-typeahead-kit-typeahead-pills-example1-result-option-remove", function(event) { + console.log('Option removed') + console.dir(event.detail) + }) + document.addEventListener("pb-typeahead-kit-typeahead-pills-example1-result-clear", function() { + console.log('All options cleared') + }) + + document.querySelector('#clear-pills').addEventListener('click', function() { + document.dispatchEvent(new CustomEvent('pb-typeahead-kit-typeahead-pills-example1:clear')) + }) +<% end %> diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color.jsx b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color.jsx new file mode 100644 index 0000000000..ac72cef3d1 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color.jsx @@ -0,0 +1,26 @@ +import React from 'react' +import { Typeahead } from 'playbook-ui' + +const options = [ + { label: 'Windows', value: '#FFA500' }, + { label: 'Siding', value: '#FF0000' }, + { label: 'Doors', value: '#00FF00' }, + { label: 'Roofs', value: '#0000FF' }, +] + +const TypeaheadWithPills = (props) => { + return ( + <> + <Typeahead + isMulti + label="Colors" + options={options} + pillColor="neutral" + placeholder="" + {...props} + /> + </> + ) +} + +export default TypeaheadWithPills diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color_rails.md b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color_rails.md new file mode 100644 index 0000000000..08f54c6329 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color_rails.md @@ -0,0 +1 @@ +Change the form pill color by passing the optional `pill_color` prop. Product, Data, and Status colors are available options. Check them out <a href="https://playbook.powerapp.cloud/kits/form_pill#form-pill-colors" target="_blank">here</a> in the Form Pill colors example. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color_react.md b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color_react.md new file mode 100644 index 0000000000..ca5350c203 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_color_react.md @@ -0,0 +1 @@ +Change the form pill color by passing the optional `pillColor` prop. Product, Data, and Status colors are available options. Check them out <a href="https://playbook.powerapp.cloud/kits/form_pill/react#form-pill-colors" target="_blank">here</a> in the Form Pill colors example. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/docs/example.yml b/playbook/app/pb_kits/playbook/pb_typeahead/docs/example.yml index aab3915516..b07abb8484 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_typeahead/docs/example.yml @@ -10,6 +10,7 @@ examples: - typeahead_multi_kit: Multi Kit Options - typeahead_error_state: Error State - typeahead_margin_bottom: Margin Bottom + - typeahead_with_pills_color: With Pills (Custom Color) react: - typeahead_default: Default @@ -25,3 +26,4 @@ examples: - typeahead_error_state: Error State - typeahead_custom_menu_list: Custom MenuList - typeahead_margin_bottom: Margin Bottom + - typeahead_with_pills_color: With Pills (Custom Color) diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/docs/index.js b/playbook/app/pb_kits/playbook/pb_typeahead/docs/index.js index 88d485f936..3484cc74ef 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_typeahead/docs/index.js @@ -11,3 +11,4 @@ export { default as TypeaheadAsyncCreateable } from './_typeahead_async_createab export { default as TypeaheadErrorState } from './_typeahead_error_state.jsx' export { default as TypeaheadCustomMenuList } from './_typeahead_custom_menu_list.jsx' export { default as TypeaheadMarginBottom } from './_typeahead_margin_bottom.jsx' +export { default as TypeaheadWithPillsColor } from './_typeahead_with_pills_color.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/typeahead.rb b/playbook/app/pb_kits/playbook/pb_typeahead/typeahead.rb index 2d40a29bc6..76db898383 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/typeahead.rb +++ b/playbook/app/pb_kits/playbook/pb_typeahead/typeahead.rb @@ -37,6 +37,9 @@ class Typeahead < Playbook::KitBase prop :margin_bottom, type: Playbook::Props::Enum, values: %w[none xxs xs sm md lg xl], default: "sm" + prop :pill_color, type: Playbook::Props::Enum, + values: %w[primary neutral success warning error info data_1 data_2 data_3 data_4 data_5 data_6 data_7 data_8 windows siding roofing doors gutters solar insulation accessories], + default: "primary" def classname default_margin_bottom = margin_bottom.present? ? "" : " mb_sm" @@ -62,6 +65,7 @@ def is_react? def typeahead_react_options base_options = { className: classname, + pillColor: pill_color, dark: dark, defaultValue: default_options, error: error, diff --git a/playbook/app/pb_kits/playbook/tokens/_colors.scss b/playbook/app/pb_kits/playbook/tokens/_colors.scss index f6837d6444..f4d8cd325d 100755 --- a/playbook/app/pb_kits/playbook/tokens/_colors.scss +++ b/playbook/app/pb_kits/playbook/tokens/_colors.scss @@ -259,6 +259,7 @@ $solar: $product_4_background !default; // deprecated $roofing: $product_5_background !default; // deprecated $gutters: $product_6_background !default; // deprecated $insulation: $product_7_background !default; // deprecated +$accessories: $product_8_background !default; // added specifically for form_pill product map $product_colors: ( windows: $windows, siding: $siding, @@ -267,6 +268,7 @@ $product_colors: ( roofing: $roofing, gutters: $gutters, insulation: $insulation, + accessories: $accessories, product_1_background: $product_1_background, product_1_highlight: $product_1_highlight, product_2_background: $product_2_background, diff --git a/playbook/lib/playbook/version.rb b/playbook/lib/playbook/version.rb index 62161161cc..dfb068ac06 100644 --- a/playbook/lib/playbook/version.rb +++ b/playbook/lib/playbook/version.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module Playbook - PREVIOUS_VERSION = "14.3.0" - VERSION = "14.3.1" + PREVIOUS_VERSION = "14.3.2" + VERSION = "14.4.0" end diff --git a/playbook/package.json b/playbook/package.json index a1947eb977..ff0845b407 100644 --- a/playbook/package.json +++ b/playbook/package.json @@ -1,6 +1,6 @@ { "name": "playbook-ui", - "version": "14.3.1", + "version": "14.4.0", "description": "Nitro's Design System", "main": "./dist/playbook.js", "types": "./dist/types/index.d.ts", @@ -35,7 +35,7 @@ "homepage": "https://github.com/powerhome/playbook#readme", "dependencies": { "@fortawesome/fontawesome-free": "^6.2.1", - "@popperjs/core": "^2.6.0", + "@popperjs/core": "^2.11.8", "flatpickr": "^4.6.13", "highcharts": "^10.0.0", "highcharts-react-official": "^3.2.0", @@ -84,7 +84,7 @@ "react-highlight-words": "^0.20.0", "react-joyride": "^2.8.2", "react-modal": "^3.12.1", - "react-popper": "^2.1.0", + "react-popper": "^2.3.0", "react-select": "5.8.0", "react-trix": "0.10.1", "react-zoom-pan-pinch": "^2.6.1", diff --git a/playbook/spec/pb_kits/playbook/kits/icon_spec.rb b/playbook/spec/pb_kits/playbook/kits/icon_spec.rb index a8ca6e5785..ad380f29bb 100644 --- a/playbook/spec/pb_kits/playbook/kits/icon_spec.rb +++ b/playbook/spec/pb_kits/playbook/kits/icon_spec.rb @@ -75,18 +75,18 @@ rotation = 90 size = "sm" - expect(subject.new(icon: icon, border: true).classname).to eq "pb_icon_kit far fa-user fa-border" - expect(subject.new(icon: icon, fixed_width: true).classname).to eq "pb_icon_kit far fa-user fa-fw" - expect(subject.new(icon: icon, flip: "horizontal").classname).to eq "pb_icon_kit far fa-user fa-flip-horizontal" - expect(subject.new(icon: icon).classname).to eq "pb_icon_kit far fa-user" - expect(subject.new(icon: icon, inverse: true).classname).to eq "pb_icon_kit far fa-user fa-inverse" - expect(subject.new(icon: icon, list_item: true).classname).to eq "pb_icon_kit far fa-user fa-li" - expect(subject.new(icon: icon, pull: pull).classname).to eq "pb_icon_kit far fa-user fa-pull-#{pull}" - expect(subject.new(icon: icon, pulse: true).classname).to eq "pb_icon_kit far fa-user fa-pulse" - expect(subject.new(icon: icon, rotation: rotation).classname).to eq "pb_icon_kit far fa-user fa-rotate-#{rotation}" - expect(subject.new(icon: icon, size: size).classname).to eq "pb_icon_kit far fa-user fa-#{size}" - expect(subject.new(icon: icon, spin: true).classname).to eq "pb_icon_kit far fa-user fa-spin" - expect(subject.new(icon: icon, classname: "additional_class").classname).to eq "pb_icon_kit far fa-user additional_class" + expect(subject.new(icon: icon, border: true, fixed_width: false).classname).to eq "pb_icon_kit far fa-user fa-border" + expect(subject.new(icon: icon).classname).to eq "pb_icon_kit far fa-user fa-fw" + expect(subject.new(icon: icon, flip: "horizontal").classname).to eq "pb_icon_kit far fa-user fa-fw fa-flip-horizontal" + expect(subject.new(icon: icon).classname).to eq "pb_icon_kit far fa-user fa-fw" + expect(subject.new(icon: icon, inverse: true).classname).to eq "pb_icon_kit far fa-user fa-fw fa-inverse" + expect(subject.new(icon: icon, list_item: true).classname).to eq "pb_icon_kit far fa-user fa-fw fa-li" + expect(subject.new(icon: icon, pull: pull).classname).to eq "pb_icon_kit far fa-user fa-fw fa-pull-#{pull}" + expect(subject.new(icon: icon, pulse: true).classname).to eq "pb_icon_kit far fa-user fa-fw fa-pulse" + expect(subject.new(icon: icon, rotation: rotation).classname).to eq "pb_icon_kit far fa-user fa-fw fa-rotate-#{rotation}" + expect(subject.new(icon: icon, size: size).classname).to eq "pb_icon_kit far fa-user fa-fw fa-#{size}" + expect(subject.new(icon: icon, spin: true).classname).to eq "pb_icon_kit far fa-user fa-fw fa-spin" + expect(subject.new(icon: icon, classname: "additional_class").classname).to eq "pb_icon_kit far fa-user fa-fw additional_class" end it "includes color class when color prop is provided", :aggregate_failures do icon = "user" diff --git a/playbook/spec/pb_kits/playbook/kits/multiple_users_stacked_spec.rb b/playbook/spec/pb_kits/playbook/kits/multiple_users_stacked_spec.rb index 41c2ceff99..5e913d37cc 100644 --- a/playbook/spec/pb_kits/playbook/kits/multiple_users_stacked_spec.rb +++ b/playbook/spec/pb_kits/playbook/kits/multiple_users_stacked_spec.rb @@ -13,8 +13,11 @@ describe "#classname" do it "returns namespaced class name", :aggregate_failures do expect(subject.new(users: []).classname).to eq "pb_multiple_users_stacked_kit" + expect(subject.new(users: [{ name: "1", image_url: "1" }]).classname).to eq "pb_multiple_users_stacked_kit_single" expect(subject.new(users: [], classname: "additional_class").classname).to eq "pb_multiple_users_stacked_kit additional_class" expect(subject.new(users: [], dark: true).classname).to eq "pb_multiple_users_stacked_kit dark" + expect(subject.new(users: [], variant: "bubble").classname).to eq "pb_multiple_users_stacked_kit_bubble" + expect(subject.new(users: [{ name: "1", image_url: "1" }], variant: "bubble").classname).to eq "pb_multiple_users_stacked_kit_single_bubble" end end diff --git a/playbook/spec/pb_kits/playbook/pb_typeahead/typeahead_spec.rb b/playbook/spec/pb_kits/playbook/pb_typeahead/typeahead_spec.rb index 88cc39743a..b24be25805 100644 --- a/playbook/spec/pb_kits/playbook/pb_typeahead/typeahead_spec.rb +++ b/playbook/spec/pb_kits/playbook/pb_typeahead/typeahead_spec.rb @@ -18,6 +18,7 @@ it { is_expected.to define_prop(:plus_icon).with_default(false) } it { is_expected.to define_prop(:search_term_minimum_length).with_default(3) } it { is_expected.to define_prop(:search_debounce_timeout).with_default(250) } + it { is_expected.to define_prop(:pill_color).with_default("primary") } describe "#typeahead_with_pills_options" do before(:each) do diff --git a/yarn.lock b/yarn.lock index 73eaed4a8c..5afe5e839d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3220,10 +3220,10 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@popperjs/core@^2.6.0": - version "2.11.6" - resolved "https://npm.powerapp.cloud/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" - integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== +"@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://npm.powerapp.cloud/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== "@popperjs/core@^2.9.0": version "2.11.7" @@ -10125,7 +10125,7 @@ react-modal@^3.12.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" -react-popper@^2.1.0: +react-popper@^2.3.0: version "2.3.0" resolved "https://npm.powerapp.cloud/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba" integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==