From 27c5726b5625e4dd54da5fa0f703fb9e3c069dd8 Mon Sep 17 00:00:00 2001 From: Jasper Furniss Date: Wed, 30 Oct 2024 14:32:52 -0400 Subject: [PATCH 01/14] [PBNTR-555] Currency Kit Updates (#3832) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **What does this PR do?** A clear and concise description with your runway ticket url. https://runway.powerhrg.com/backlog_items/PBNTR-555 **Screenshots:** Screenshots to visualize your addition/change Screenshot 2024-10-22 at 3 53 03 PM **How to test?** Steps to confirm the desired behavior: 1. Go to bottom of Currency kit on rails & react. 2. See new doc examples highlighting the new comma_separator prop. #### Checklist: - [X] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [X] **DEPLOY** I have added the `milano` label to show I'm ready for a review. - [X] **TESTS** I have added test coverage to my code. --- .../workflows/github-actions-check-labels.yml | 0 .../playbook/pb_currency/_currency.tsx | 22 ++++++--- .../pb_kits/playbook/pb_currency/currency.rb | 49 ++++++++++++++----- .../playbook/pb_currency/currency.test.js | 35 +++++++++++++ .../docs/_currency_comma_separator.html.erb | 7 +++ .../docs/_currency_comma_separator.jsx | 18 +++++++ .../docs/_currency_comma_separator.md | 3 ++ .../playbook/pb_currency/docs/example.yml | 4 +- .../playbook/pb_currency/docs/index.js | 1 + .../playbook/pb_currency/currency_spec.rb | 9 ++++ 10 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/github-actions-check-labels.yml create mode 100644 playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb create mode 100644 playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx create mode 100644 playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md diff --git a/.github/workflows/github-actions-check-labels.yml b/.github/workflows/github-actions-check-labels.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx b/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx index f0673c777b..c03f683541 100644 --- a/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx +++ b/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx @@ -26,6 +26,7 @@ type CurrencyProps = { variant?: 'default' | 'light' | 'bold', unit?: string, unstyled?: boolean, + commaSeparator?: boolean, } const sizes: {lg: 1, md: 3, sm: 4} = { @@ -53,6 +54,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => { variant = 'default', dark = false, unstyled = false, + commaSeparator = false, } = props const emphasizedClass = emphasized ? '' : '_deemphasized' @@ -74,7 +76,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => { className ) - const getFormattedNumber = (input: number | any ) => new Intl.NumberFormat('en-US', { + const getFormattedNumber = (input: number | any) => new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1, }).format(input) @@ -88,12 +90,20 @@ const Currency = (props: CurrencyProps): React.ReactElement => { return isAmount ? num.slice(0, -1) : isUnit ? num.slice(-1) : '' } - const getMatchingDecimalAmount = decimals === "matching" ? amount : whole, - getMatchingDecimalValue = decimals === "matching" ? '' : `.${decimal}` + const getMatchingDecimalAmount = decimals === "matching" ? amount : whole + const getMatchingDecimalValue = decimals === "matching" ? '' : `.${decimal}` - const getAmount = abbreviate ? getAbbreviatedValue('amount') : getMatchingDecimalAmount, - getAbbreviation = abbreviate ? getAbbreviatedValue('unit') : null, - getDecimalValue = abbreviate ? '' : getMatchingDecimalValue + const formatAmount = (amount: string) => { + if (!commaSeparator) return amount; + + const [wholePart, decimalPart] = amount.split('.'); + const formattedWhole = new Intl.NumberFormat('en-US').format(parseInt(wholePart)); + return decimalPart ? `${formattedWhole}.${decimalPart}` : formattedWhole; + } + + const getAmount = abbreviate ? getAbbreviatedValue('amount') : formatAmount(getMatchingDecimalAmount) + const getAbbreviation = abbreviate ? getAbbreviatedValue('unit') : null + const getDecimalValue = abbreviate ? '' : getMatchingDecimalValue return (
{ expect(currencyKit.querySelector('.pb_currency_value')).toHaveTextContent('320') expect(currencyKit.querySelector('.unit')).toHaveTextContent('.20') }) + + +test('commaSeparator prop returns comma separated amount', () => { + render( + + ) + expect(screen.getByTestId('comma-test')).toHaveTextContent('1,234,567,890') +}) + +test('commaSeparator prop returns comma separated amount with decimals', () => { + render( + + ) + expect(screen.getByTestId('comma-test-decimals')).toHaveTextContent('1,234,567,890.12') +}) + +test('commaSeparator prop returns comma separated amount with decimals="matching"', () => { + render( + + ) + expect(screen.getByTestId('comma-test-decimals-matching')).toHaveTextContent('1,234,567,890.12') +}) diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb new file mode 100644 index 0000000000..48f0548013 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb @@ -0,0 +1,7 @@ +<%= pb_rails("currency", props: { + amount: '1234567.89', + comma_separator: true, + size: 'lg', + emphasized: false, + decimals: 'matching', +}) %> diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx new file mode 100644 index 0000000000..851cf52a10 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx @@ -0,0 +1,18 @@ +import React from "react" + +import Currency from "../_currency" + +const CurrencyCommaSeparator = (props) => { + return ( + + ) +} + +export default CurrencyCommaSeparator diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md new file mode 100644 index 0000000000..b2c926c110 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md @@ -0,0 +1,3 @@ +The optional `commaSeparator` can be used to auto-format the use of commas as a thousands separator. + +**NOTE:** If the value passed into the `amount` prop is already comma-dilineated, it will not add additional commas. diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml b/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml index a8354bce15..8568c2a514 100644 --- a/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml @@ -8,7 +8,8 @@ examples: - currency_abbreviated: Abbreviate Larger Amounts - currency_matching_decimals: Matching Decimals - currency_unstyled: Unstyled - + - currency_comma_separator: Comma Separator + react: - currency_variants: Variants - currency_size: Size @@ -17,6 +18,7 @@ examples: - currency_abbreviated: Abbreviate Larger Amounts - currency_matching_decimals: Matching Decimals - currency_unstyled: Unstyled + - currency_comma_separator: Comma Separator swift: - currency_size_swift: Size diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/index.js b/playbook/app/pb_kits/playbook/pb_currency/docs/index.js index e0ab2b0b99..86ae571909 100644 --- a/playbook/app/pb_kits/playbook/pb_currency/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/index.js @@ -5,3 +5,4 @@ export { default as CurrencyNoSymbol } from './_currency_no_symbol.jsx' export { default as CurrencyAbbreviated } from './_currency_abbreviated.jsx' export { default as CurrencyMatchingDecimals } from './_currency_matching_decimals.jsx' export { default as CurrencyUnstyled } from './_currency_unstyled.jsx' +export { default as CurrencyCommaSeparator } from './_currency_comma_separator.jsx' diff --git a/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb b/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb index 6a5d8bf3e6..f69e4a8449 100644 --- a/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb +++ b/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb @@ -15,6 +15,7 @@ it { is_expected.to define_enum_prop(:variant).with_default("default").with_values("default", "light", "bold") } it { is_expected.to define_prop(:abbreviate).with_default(false).of_type(Playbook::Props::Boolean) } it { is_expected.to define_enum_prop(:decimals).with_default("default").with_values("default", "matching") } + it { is_expected.to define_prop(:comma_separator).with_default(false).of_type(Playbook::Props::Boolean) } describe "#classname" do it "returns namespaced class name", :aggregate_failures do @@ -75,4 +76,12 @@ expect(num.body_props[:text]).to eq ".20" end end + + describe "when prop commaSeparator is set to true" do + it "returns comma separated amount" do + num = subject.new(comma_separator: true, amount: "1234567890") + + expect(num.title_props[:text]).to eq "1,234,567,890" + end + end end From 5c32dbde8a406a1a4920e723b97ccc32ac5e75ca Mon Sep 17 00:00:00 2001 From: Jasper Furniss Date: Wed, 30 Oct 2024 15:59:48 -0400 Subject: [PATCH 02/14] Revert "[PBNTR-555] Currency Kit Updates" (#3866) Reverts powerhome/playbook#3832 --- .../workflows/github-actions-check-labels.yml | 0 .../playbook/pb_currency/_currency.tsx | 22 +++------ .../pb_kits/playbook/pb_currency/currency.rb | 49 +++++-------------- .../playbook/pb_currency/currency.test.js | 35 ------------- .../docs/_currency_comma_separator.html.erb | 7 --- .../docs/_currency_comma_separator.jsx | 18 ------- .../docs/_currency_comma_separator.md | 3 -- .../playbook/pb_currency/docs/example.yml | 4 +- .../playbook/pb_currency/docs/index.js | 1 - .../playbook/pb_currency/currency_spec.rb | 9 ---- 10 files changed, 18 insertions(+), 130 deletions(-) delete mode 100644 .github/workflows/github-actions-check-labels.yml delete mode 100644 playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb delete mode 100644 playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx delete mode 100644 playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md diff --git a/.github/workflows/github-actions-check-labels.yml b/.github/workflows/github-actions-check-labels.yml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx b/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx index c03f683541..f0673c777b 100644 --- a/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx +++ b/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx @@ -26,7 +26,6 @@ type CurrencyProps = { variant?: 'default' | 'light' | 'bold', unit?: string, unstyled?: boolean, - commaSeparator?: boolean, } const sizes: {lg: 1, md: 3, sm: 4} = { @@ -54,7 +53,6 @@ const Currency = (props: CurrencyProps): React.ReactElement => { variant = 'default', dark = false, unstyled = false, - commaSeparator = false, } = props const emphasizedClass = emphasized ? '' : '_deemphasized' @@ -76,7 +74,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => { className ) - const getFormattedNumber = (input: number | any) => new Intl.NumberFormat('en-US', { + const getFormattedNumber = (input: number | any ) => new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1, }).format(input) @@ -90,20 +88,12 @@ const Currency = (props: CurrencyProps): React.ReactElement => { return isAmount ? num.slice(0, -1) : isUnit ? num.slice(-1) : '' } - const getMatchingDecimalAmount = decimals === "matching" ? amount : whole - const getMatchingDecimalValue = decimals === "matching" ? '' : `.${decimal}` + const getMatchingDecimalAmount = decimals === "matching" ? amount : whole, + getMatchingDecimalValue = decimals === "matching" ? '' : `.${decimal}` - const formatAmount = (amount: string) => { - if (!commaSeparator) return amount; - - const [wholePart, decimalPart] = amount.split('.'); - const formattedWhole = new Intl.NumberFormat('en-US').format(parseInt(wholePart)); - return decimalPart ? `${formattedWhole}.${decimalPart}` : formattedWhole; - } - - const getAmount = abbreviate ? getAbbreviatedValue('amount') : formatAmount(getMatchingDecimalAmount) - const getAbbreviation = abbreviate ? getAbbreviatedValue('unit') : null - const getDecimalValue = abbreviate ? '' : getMatchingDecimalValue + const getAmount = abbreviate ? getAbbreviatedValue('amount') : getMatchingDecimalAmount, + getAbbreviation = abbreviate ? getAbbreviatedValue('unit') : null, + getDecimalValue = abbreviate ? '' : getMatchingDecimalValue return (
{ expect(currencyKit.querySelector('.pb_currency_value')).toHaveTextContent('320') expect(currencyKit.querySelector('.unit')).toHaveTextContent('.20') }) - - -test('commaSeparator prop returns comma separated amount', () => { - render( - - ) - expect(screen.getByTestId('comma-test')).toHaveTextContent('1,234,567,890') -}) - -test('commaSeparator prop returns comma separated amount with decimals', () => { - render( - - ) - expect(screen.getByTestId('comma-test-decimals')).toHaveTextContent('1,234,567,890.12') -}) - -test('commaSeparator prop returns comma separated amount with decimals="matching"', () => { - render( - - ) - expect(screen.getByTestId('comma-test-decimals-matching')).toHaveTextContent('1,234,567,890.12') -}) diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb deleted file mode 100644 index 48f0548013..0000000000 --- a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -<%= pb_rails("currency", props: { - amount: '1234567.89', - comma_separator: true, - size: 'lg', - emphasized: false, - decimals: 'matching', -}) %> diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx deleted file mode 100644 index 851cf52a10..0000000000 --- a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react" - -import Currency from "../_currency" - -const CurrencyCommaSeparator = (props) => { - return ( - - ) -} - -export default CurrencyCommaSeparator diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md deleted file mode 100644 index b2c926c110..0000000000 --- a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md +++ /dev/null @@ -1,3 +0,0 @@ -The optional `commaSeparator` can be used to auto-format the use of commas as a thousands separator. - -**NOTE:** If the value passed into the `amount` prop is already comma-dilineated, it will not add additional commas. diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml b/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml index 8568c2a514..a8354bce15 100644 --- a/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml @@ -8,8 +8,7 @@ examples: - currency_abbreviated: Abbreviate Larger Amounts - currency_matching_decimals: Matching Decimals - currency_unstyled: Unstyled - - currency_comma_separator: Comma Separator - + react: - currency_variants: Variants - currency_size: Size @@ -18,7 +17,6 @@ examples: - currency_abbreviated: Abbreviate Larger Amounts - currency_matching_decimals: Matching Decimals - currency_unstyled: Unstyled - - currency_comma_separator: Comma Separator swift: - currency_size_swift: Size diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/index.js b/playbook/app/pb_kits/playbook/pb_currency/docs/index.js index 86ae571909..e0ab2b0b99 100644 --- a/playbook/app/pb_kits/playbook/pb_currency/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/index.js @@ -5,4 +5,3 @@ export { default as CurrencyNoSymbol } from './_currency_no_symbol.jsx' export { default as CurrencyAbbreviated } from './_currency_abbreviated.jsx' export { default as CurrencyMatchingDecimals } from './_currency_matching_decimals.jsx' export { default as CurrencyUnstyled } from './_currency_unstyled.jsx' -export { default as CurrencyCommaSeparator } from './_currency_comma_separator.jsx' diff --git a/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb b/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb index f69e4a8449..6a5d8bf3e6 100644 --- a/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb +++ b/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb @@ -15,7 +15,6 @@ it { is_expected.to define_enum_prop(:variant).with_default("default").with_values("default", "light", "bold") } it { is_expected.to define_prop(:abbreviate).with_default(false).of_type(Playbook::Props::Boolean) } it { is_expected.to define_enum_prop(:decimals).with_default("default").with_values("default", "matching") } - it { is_expected.to define_prop(:comma_separator).with_default(false).of_type(Playbook::Props::Boolean) } describe "#classname" do it "returns namespaced class name", :aggregate_failures do @@ -76,12 +75,4 @@ expect(num.body_props[:text]).to eq ".20" end end - - describe "when prop commaSeparator is set to true" do - it "returns comma separated amount" do - num = subject.new(comma_separator: true, amount: "1234567890") - - expect(num.title_props[:text]).to eq "1,234,567,890" - end - end end From b5cc921363c77f8ec219e9ba2b9bbd7f5d2deab2 Mon Sep 17 00:00:00 2001 From: Rachel Radford <54749071+RachelRadford21@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:08:17 -0400 Subject: [PATCH 03/14] [PBIOS-602] Swift Changelog Update (#3854) **What does this PR do?** A clear and concise description with your runway ticket url. [PBIOS-602] Swift Changelog Update **Screenshots:** Screenshots to visualize your addition/change **How to test?** Steps to confirm the desired behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See addition/change #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. - [x] **TESTS** I have added test coverage to my code. --- playbook/SWIFT_CHANGELOG.md | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/playbook/SWIFT_CHANGELOG.md b/playbook/SWIFT_CHANGELOG.md index 7a0624a559..24bd2c6434 100644 --- a/playbook/SWIFT_CHANGELOG.md +++ b/playbook/SWIFT_CHANGELOG.md @@ -1,3 +1,56 @@ +# 🚀 Playbook Swift 6.4.0: Enhanced Buttons, Truncations, and Dynamic Typeahead! 🚀 +##### Oct 18, 2024 +![image](https://github.com/user-attachments/assets/875ae784-bb68-47e1-ae67-3d25e90f427e) + +The latest release, Playbook Swift 6.4.0, brings new enhancements focused on flexibility, user experience, and seamless integrations. Key highlights include the new **Destructive Button**, adjustments to **Typeahead** for height and list display, and improved truncation in the User Kit for a cleaner, more responsive UI. + +[6.4.0](https://github.com/powerhome/playbook-swift/tree/6.4.0) full list of changes: + +### Playbook Changes + +#### Kit Enhancements +- **Destructive Button**: A new button style designed for critical actions. (#457) +- **Height Adjusted Dropdown**: Improved dropdown to better fit varied content heights. (#450) +- **User Kit Truncation**: Tighter, more dynamic user name displays. (#458) +- **Add Props to PBMessage**: Additional properties to further customize PBMessage. (#456) +- **Typeahead Enhancements**: + - **Show No Option**: Now includes messaging when no options are available. (#455) + - **Section List**: Categorized options for a more organized typeahead. (#459) + - **Initial Value Support**: Set a starting value in PBSwift Typeahead. + - **Truncated User Name Support**: Cleaner user name display in User Kit. + +#### Bug Fixes +- **Message Component Cursor Fix**: Improved cursor behavior for smooth typing experiences. (#454) + +### Documentation Updates +- **Typeahead Enhancements**: Documented updates for scrollable lists, dropdown height adjustments, conversation name display, and improved conversation management options. + +### Connect-Specific +- **Conditional Popover Handler**: Fixed the “Close” button issue with a new conditional handler. (#565) + +🔗 **Full Changelog**: https://github.com/powerhome/playbook-swift/compare/6.3.1...6.4.0 + + + +# ✨ Dynamic Status Indicators & Interactive Messaging! ✨ +##### Sept 20, 2024 +![image](https://github.com/user-attachments/assets/84b6e16c-9fa4-45ba-b00f-6ffa223dbbb4) + +We’re excited to introduce Playbook Swift 6.3.0, featuring two key updates: a customizable PBAvatar status indicator size and new interactivity for the message kit, delivering more responsive, intuitive user experiences. Here’s what’s new: + +[6.3.0](https://github.com/powerhome/playbook-swift/tree/6.3.0) full list of changes: + +**Kit Enhancements** +- **PBAvatar Status Indicator Size**: Adjust and style status indicators with more control for a sharper, polished look. #525 +- **Handle Click on Message Kit**: Respond to user interactions seamlessly, elevating message engagement. #526 +- **Dark Mode Reaction Button Improvements**: Enhanced visibility with border and button refinements for Dark Mode. #508, #528 + +### Bug Fix +- **Online Status Color**: Ensuring accurate color representation for improved visual clarity. #566 + + +🔗 **Full Changelog**: https://github.com/powerhome/playbook-swift/compare/6.2.0...6.3.0 + # ✨ Enhance, Customize, and Expand! ✨ ##### Aug 28, 2024 From c965ab789e1d3048ee52fa9f5432598aba91552b Mon Sep 17 00:00:00 2001 From: Jasper Furniss Date: Thu, 31 Oct 2024 09:31:34 -0400 Subject: [PATCH 04/14] [PBNTR-555] Currency Kit Comma Separator (#3867) **What does this PR do?** A clear and concise description with your runway ticket url. **Screenshots:** Screenshots to visualize your addition/change **How to test?** Steps to confirm the desired behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See addition/change #### Checklist: - [ ] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [ ] **DEPLOY** I have added the `milano` label to show I'm ready for a review. - [ ] **TESTS** I have added test coverage to my code. --- .../workflows/github-actions-check-labels.yml | 0 .../playbook/pb_currency/_currency.tsx | 22 ++++++--- .../pb_kits/playbook/pb_currency/currency.rb | 49 ++++++++++++++----- .../playbook/pb_currency/currency.test.js | 35 +++++++++++++ .../docs/_currency_comma_separator.html.erb | 7 +++ .../docs/_currency_comma_separator.jsx | 18 +++++++ .../docs/_currency_comma_separator.md | 3 ++ .../playbook/pb_currency/docs/example.yml | 4 +- .../playbook/pb_currency/docs/index.js | 1 + .../playbook/pb_currency/currency_spec.rb | 9 ++++ 10 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/github-actions-check-labels.yml create mode 100644 playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb create mode 100644 playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx create mode 100644 playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md diff --git a/.github/workflows/github-actions-check-labels.yml b/.github/workflows/github-actions-check-labels.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx b/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx index f0673c777b..c03f683541 100644 --- a/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx +++ b/playbook/app/pb_kits/playbook/pb_currency/_currency.tsx @@ -26,6 +26,7 @@ type CurrencyProps = { variant?: 'default' | 'light' | 'bold', unit?: string, unstyled?: boolean, + commaSeparator?: boolean, } const sizes: {lg: 1, md: 3, sm: 4} = { @@ -53,6 +54,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => { variant = 'default', dark = false, unstyled = false, + commaSeparator = false, } = props const emphasizedClass = emphasized ? '' : '_deemphasized' @@ -74,7 +76,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => { className ) - const getFormattedNumber = (input: number | any ) => new Intl.NumberFormat('en-US', { + const getFormattedNumber = (input: number | any) => new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1, }).format(input) @@ -88,12 +90,20 @@ const Currency = (props: CurrencyProps): React.ReactElement => { return isAmount ? num.slice(0, -1) : isUnit ? num.slice(-1) : '' } - const getMatchingDecimalAmount = decimals === "matching" ? amount : whole, - getMatchingDecimalValue = decimals === "matching" ? '' : `.${decimal}` + const getMatchingDecimalAmount = decimals === "matching" ? amount : whole + const getMatchingDecimalValue = decimals === "matching" ? '' : `.${decimal}` - const getAmount = abbreviate ? getAbbreviatedValue('amount') : getMatchingDecimalAmount, - getAbbreviation = abbreviate ? getAbbreviatedValue('unit') : null, - getDecimalValue = abbreviate ? '' : getMatchingDecimalValue + const formatAmount = (amount: string) => { + if (!commaSeparator) return amount; + + const [wholePart, decimalPart] = amount.split('.'); + const formattedWhole = new Intl.NumberFormat('en-US').format(parseInt(wholePart)); + return decimalPart ? `${formattedWhole}.${decimalPart}` : formattedWhole; + } + + const getAmount = abbreviate ? getAbbreviatedValue('amount') : formatAmount(getMatchingDecimalAmount) + const getAbbreviation = abbreviate ? getAbbreviatedValue('unit') : null + const getDecimalValue = abbreviate ? '' : getMatchingDecimalValue return (
{ expect(currencyKit.querySelector('.pb_currency_value')).toHaveTextContent('320') expect(currencyKit.querySelector('.unit')).toHaveTextContent('.20') }) + + +test('commaSeparator prop returns comma separated amount', () => { + render( + + ) + expect(screen.getByTestId('comma-test')).toHaveTextContent('1,234,567,890') +}) + +test('commaSeparator prop returns comma separated amount with decimals', () => { + render( + + ) + expect(screen.getByTestId('comma-test-decimals')).toHaveTextContent('1,234,567,890.12') +}) + +test('commaSeparator prop returns comma separated amount with decimals="matching"', () => { + render( + + ) + expect(screen.getByTestId('comma-test-decimals-matching')).toHaveTextContent('1,234,567,890.12') +}) diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb new file mode 100644 index 0000000000..48f0548013 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb @@ -0,0 +1,7 @@ +<%= pb_rails("currency", props: { + amount: '1234567.89', + comma_separator: true, + size: 'lg', + emphasized: false, + decimals: 'matching', +}) %> diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx new file mode 100644 index 0000000000..851cf52a10 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx @@ -0,0 +1,18 @@ +import React from "react" + +import Currency from "../_currency" + +const CurrencyCommaSeparator = (props) => { + return ( + + ) +} + +export default CurrencyCommaSeparator diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md new file mode 100644 index 0000000000..b2c926c110 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md @@ -0,0 +1,3 @@ +The optional `commaSeparator` can be used to auto-format the use of commas as a thousands separator. + +**NOTE:** If the value passed into the `amount` prop is already comma-dilineated, it will not add additional commas. diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml b/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml index a8354bce15..8568c2a514 100644 --- a/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/example.yml @@ -8,7 +8,8 @@ examples: - currency_abbreviated: Abbreviate Larger Amounts - currency_matching_decimals: Matching Decimals - currency_unstyled: Unstyled - + - currency_comma_separator: Comma Separator + react: - currency_variants: Variants - currency_size: Size @@ -17,6 +18,7 @@ examples: - currency_abbreviated: Abbreviate Larger Amounts - currency_matching_decimals: Matching Decimals - currency_unstyled: Unstyled + - currency_comma_separator: Comma Separator swift: - currency_size_swift: Size diff --git a/playbook/app/pb_kits/playbook/pb_currency/docs/index.js b/playbook/app/pb_kits/playbook/pb_currency/docs/index.js index e0ab2b0b99..86ae571909 100644 --- a/playbook/app/pb_kits/playbook/pb_currency/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_currency/docs/index.js @@ -5,3 +5,4 @@ export { default as CurrencyNoSymbol } from './_currency_no_symbol.jsx' export { default as CurrencyAbbreviated } from './_currency_abbreviated.jsx' export { default as CurrencyMatchingDecimals } from './_currency_matching_decimals.jsx' export { default as CurrencyUnstyled } from './_currency_unstyled.jsx' +export { default as CurrencyCommaSeparator } from './_currency_comma_separator.jsx' diff --git a/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb b/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb index 6a5d8bf3e6..f69e4a8449 100644 --- a/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb +++ b/playbook/spec/pb_kits/playbook/pb_currency/currency_spec.rb @@ -15,6 +15,7 @@ it { is_expected.to define_enum_prop(:variant).with_default("default").with_values("default", "light", "bold") } it { is_expected.to define_prop(:abbreviate).with_default(false).of_type(Playbook::Props::Boolean) } it { is_expected.to define_enum_prop(:decimals).with_default("default").with_values("default", "matching") } + it { is_expected.to define_prop(:comma_separator).with_default(false).of_type(Playbook::Props::Boolean) } describe "#classname" do it "returns namespaced class name", :aggregate_failures do @@ -75,4 +76,12 @@ expect(num.body_props[:text]).to eq ".20" end end + + describe "when prop commaSeparator is set to true" do + it "returns comma separated amount" do + num = subject.new(comma_separator: true, amount: "1234567890") + + expect(num.title_props[:text]).to eq "1,234,567,890" + end + end end From f8db614fd5b9e913b47405a00cee91c1d58360d9 Mon Sep 17 00:00:00 2001 From: Gary Kang <42440452+kangaree@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:22:49 -0400 Subject: [PATCH 05/14] [PLAY-1485] Fix overflow container bug by using padding instead of outline (#3807) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **What does this PR do?** In [PLAY-829](https://runway.powerhrg.com/backlog_items/PLAY-829), we removed the buffer to fix content alignment issues and added an outline so the content would not 'jump' (which was handled originally by the buffer). - Fix overflow container bug by using padding instead of outline - Subtract 1px of padding in the selected label container to account for the extra 1px border of selected label - Do this for all padding sizes xxs to xl - For "selectable cards", keep an outline as the content is different (a padding 0 div wrapped around flex and cards) - Fix the dark mode for rails and apply it to the card **Screenshots:** Screenshots to visualize your addition/change ![Screenshot 2024-10-30 at 10 25 07 AM](https://github.com/user-attachments/assets/68b7efcf-3775-4d15-b70d-cce39b40f698) ![Screenshot 2024-10-23 at 9 15 54 AM](https://github.com/user-attachments/assets/a447bd7b-2bbb-4aa0-97b8-d7804bc7a19c) **How to test?** Steps to confirm the desired behavior: 1. Go to the Playbook Website 2. Go to the Selectable Card kit 3. Click the cards 4. Make sure the content does not "jump" or shift around and stays in place 5. Go to the Nitro test env 6. Use the Impersonation Flyout 7. Search by title so multiple results show (e.g., "Nitro Quality Ninja") 8. The selected card should not be cut off 9. If you select a card, the content should not "jump" or shift around 10. Since there are multiple employees, everything should fit in the overlay- the impersonate and cancel button should fit on the overlay and not be below a scroll. #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. --- .../pb_kits/playbook/pb_card/_card_mixin.scss | 3 +- .../pb_selectable_card/_selectable_card.scss | 68 ++++++++++++++++++- .../pb_selectable_card/_selectable_card.tsx | 1 + .../selectable_card.html.erb | 2 +- .../pb_selectable_card/selectable_card.rb | 6 +- 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/playbook/app/pb_kits/playbook/pb_card/_card_mixin.scss b/playbook/app/pb_kits/playbook/pb_card/_card_mixin.scss index a3781bf2ac..f15d002ee0 100755 --- a/playbook/app/pb_kits/playbook/pb_card/_card_mixin.scss +++ b/playbook/app/pb_kits/playbook/pb_card/_card_mixin.scss @@ -28,8 +28,7 @@ $pb_card_header_colors: map-merge(map-merge($product_colors, $additional_colors) @mixin pb_card_selected($border_color: $primary) { border-color: $border_color; - border-width: $pb_card_border_width; - outline: 1px solid $border_color; + border-width: $pb_card_border_width * 2; } @mixin pb_card_selected_dark { diff --git a/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.scss b/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.scss index f2dda25c0d..e84ccb6d81 100644 --- a/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.scss +++ b/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.scss @@ -10,6 +10,24 @@ $pb_selectable_card_indicator_size: 22px; $pb_selectable_card_border: 2px; +$pb_selectable_space_classes: ( + xxs: $space_xxs, + xs: $space_xs, + sm: $space_sm, + md: $space_md, + lg: $space_lg, + xl: $space_xl, +); +$pb_selectable_paddings: ( + p: "padding", + pr: "padding-right", + pl: "padding-left", + pt: "padding-top", + pb: "padding-bottom", + px: ("padding-left", "padding-right"), + py: ("padding-top", "padding-bottom") +); + [class^=pb_selectable_card_kit] { display: block; margin-bottom: 0; @@ -28,7 +46,6 @@ $pb_selectable_card_border: 2px; padding: $space_sm; margin-bottom: $space_sm; cursor: pointer; - outline: 1px solid transparent; @media (hover:hover) { &:hover { @@ -74,6 +91,7 @@ $pb_selectable_card_border: 2px; position: relative; @include pb_card_selected; + padding: calc(#{$space_sm} - 1px); transition-property: none; transition-duration: 0s; @@ -88,6 +106,54 @@ $pb_selectable_card_border: 2px; background-color: $royal; } } + + // Selected card has 1px more border + // Remove 1px so content does not "jump" + @each $position_name, + $position in $pb_selectable_paddings { + @each $space_name, + $space in $pb_selectable_space_classes { + ~ label.#{$position_name}_#{$space_name} { + @if type-of($position)=="list" { + @each $coordinate in $position { + #{$coordinate}: calc(#{$space} - 1px) !important; + } + } + + @else { + #{$position}: calc(#{$space} - 1px) !important; + } + } + } + } + } + } + + &.display_input { + input[type="checkbox"], + input[type="radio"] { + &:checked { + ~label { + border-width: $pb_card_border_width; + outline: 1px solid $primary; + } + + } + } + + > label { + outline: 1px solid transparent; + padding: $space_sm; + } + + &.dark { + input[type="checkbox"], + input[type="radio"] { + &:checked ~ label { + border-width: $pb_card_border_width; + outline: 1px solid $primary; + } + } } } diff --git a/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.tsx b/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.tsx index 923f602710..8b53177f62 100644 --- a/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.tsx +++ b/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.tsx @@ -67,6 +67,7 @@ const SelectableCard = (props: SelectableCardProps) => { 'disabled': disabled, 'enabled': !disabled, }), + variant === 'displayInput' ? 'display_input' : '', { error }, dark ? 'dark' : '', className diff --git a/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb b/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb index 0618d35d17..e3f997a773 100644 --- a/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb +++ b/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb @@ -25,7 +25,7 @@ <% end %>
- <%= pb_rails("card", props: { padding: "sm", status: object.status, border_none: true }) do %> + <%= pb_rails("card", props: { padding: "sm", status: object.status, border_none: true, dark: object.dark }) do %> <% if content.nil? %> <%= pb_rails("body", props: { text: object.text }) %> <% else %> diff --git a/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.rb b/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.rb index a9934da1d4..6b6b2e6158 100644 --- a/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.rb +++ b/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.rb @@ -25,7 +25,7 @@ class SelectableCard < Playbook::KitBase def classname [ - generate_classname_without_spacing("pb_selectable_card_kit", checked_class, enable_disabled_class), + generate_classname_without_spacing("pb_selectable_card_kit", checked_class, enable_disabled_class) + display_input_class, error_class, dark_props, ].compact.join(" ") @@ -79,6 +79,10 @@ def enable_disabled_class def error_class error ? "error" : nil end + + def display_input_class + variant == "display_input" ? " display_input" : "" + end end end end From b85fa1e0b9e07e57430c8b802571d1f81bce12bd Mon Sep 17 00:00:00 2001 From: Mark Rosenberg <38965626+markdoeswork@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:21:32 -0400 Subject: [PATCH 06/14] [PLAY-1571] Add dependencies to getting started page (#3864) **What does this PR do?** A clear and concise description with your runway ticket url. Runway https://runway.powerhrg.com/backlog_items/PLAY-1571 Find all the dependancies for our kits and put them on a page Some of them need the plabyook consumer to add a library to their app, others ship with playbook check it out https://pr3864.playbook.beta.px.powerapp.cloud/guides/getting_started/dependencies ![screenshot-pr3864_playbook_beta_px_powerapp_cloud-2024_10_30-15_11_44](https://github.com/user-attachments/assets/52deb7b1-ad74-47d0-8474-15ca37efba5f) --- .../guides/getting_started/dependencies.md | 52 +++++++++++++++++++ .../config/initializers/global_variables.rb | 7 +-- 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 playbook-website/app/views/guides/getting_started/dependencies.md diff --git a/playbook-website/app/views/guides/getting_started/dependencies.md b/playbook-website/app/views/guides/getting_started/dependencies.md new file mode 100644 index 0000000000..3323271988 --- /dev/null +++ b/playbook-website/app/views/guides/getting_started/dependencies.md @@ -0,0 +1,52 @@ +--- +title: Dependencies +icon: code +description: Some of our kits require additional libraries to run properly. +--- + +## Unbundled Dependencies + +These kits require you to install additional libraries to get full functionality. + +To install them add them to your project using `yarn add`, `npm install`, or manually add them to your `package.json` file. + +| Kit | Kit Link | NPM Link(s) | Dependency(s) | +|---------------------|-----------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|---------------------------------------------| +| **Icon** | [Icon](https://playbook.powerapp.cloud/kits/icon/react) | [fontawesome-free](https://www.npmjs.com/package/fontawesome-free) | fontawesome-free | +| **Icon Circle** | [Icon Circle](https://playbook.powerapp.cloud/kits/icon_circle/react) | [fontawesome-free](https://www.npmjs.com/package/fontawesome-free) | fontawesome-free | +| **Icon Stat Value** | [Icon Stat Value](https://playbook.powerapp.cloud/kits/icon_stat_value/react) | [fontawesome-free](https://www.npmjs.com/package/fontawesome-free) | fontawesome-free | +| **Icon Value** | [Icon Value](https://playbook.powerapp.cloud/kits/icon_value/react) | [fontawesome-free](https://www.npmjs.com/package/fontawesome-free) | fontawesome-free | +| **Map** | [Map](https://playbook.powerapp.cloud/kits/map/react) | [maplibre-gl](https://www.npmjs.com/package/maplibre-gl) | maplibre-gl | +| **Rich Text Editor**
(TipTap Editor) | [Rich Text Editor](https://playbook.powerapp.cloud/kits/rich_text_editor/react) | - [@tiptap/core](https://www.npmjs.com/package/@tiptap/core)
- [@tiptap/react](https://www.npmjs.com/package/@tiptap/react)
- [@tiptap/starter-kit](https://www.npmjs.com/package/@tiptap/starter-kit)
- [@tiptap/extension-document](https://www.npmjs.com/package/@tiptap/extension-document)
- [@tiptap/extension-highlight](https://www.npmjs.com/package/@tiptap/extension-highlight)
- [@tiptap/extension-horizontal-rule](https://www.npmjs.com/package/@tiptap/extension-horizontal-rule)
- [@tiptap/extension-link](https://www.npmjs.com/package/@tiptap/extension-link)
- [@tiptap/extension-paragraph](https://www.npmjs.com/package/@tiptap/extension-paragraph)
- [@tiptap/extension-text](https://www.npmjs.com/package/@tiptap/extension-text)
- [@tiptap/pm](https://www.npmjs.com/package/@tiptap/pm) | - @tiptap/core
- @tiptap/react
- @tiptap/starter-kit
- @tiptap/extension-document
- @tiptap/extension-highlight
- @tiptap/extension-horizontal-rule
- @tiptap/extension-link
- @tiptap/extension-paragraph
- @tiptap/extension-text
- @tiptap/pm | + +## Bundled Dependencies + +These kits use dependencies that are bundled with them; no additional installation is required. + +| Kit | Kit Link | NPM Link(s) | Dependency(s) | +|------------------------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|-----------------------------------------| +| **Advanced Table** | [Advanced Table](https://playbook.powerapp.cloud/kits/advanced_table/react) | [@tanstack/react-table](https://www.npmjs.com/package/@tanstack/react-table) | @tanstack/react-table | +| **Bar Graph** | [Bar Graph](https://playbook.powerapp.cloud/kits/bar_graph/react) | [highcharts](https://www.npmjs.com/package/highcharts),
[highcharts-react-official](https://www.npmjs.com/package/highcharts-react-official) | highcharts,
highcharts-react-official | +| **Circle Chart** | [Circle Chart](https://playbook.powerapp.cloud/kits/circle_chart/react) | [highcharts](https://www.npmjs.com/package/highcharts),
[highcharts-react-official](https://www.npmjs.com/package/highcharts-react-official) | highcharts,
highcharts-react-official | +| **Date Picker** | [Date Picker](https://playbook.powerapp.cloud/kits/date_picker/react) | [flatpickr](https://www.npmjs.com/package/flatpickr) | flatpickr | +| **Dialog** | [Dialog](https://playbook.powerapp.cloud/kits/dialog/react) | [react-modal](https://www.npmjs.com/package/react-modal) | react-modal | +| **File Upload** | [File Upload](https://playbook.powerapp.cloud/kits/file_upload/react) | [react-dropzone](https://www.npmjs.com/package/react-dropzone) | react-dropzone | +| **Filter** | [Filter](https://playbook.powerapp.cloud/kits/filter/react) | [react-popper](https://www.npmjs.com/package/react-popper) | react-popper | +| **Gauge** | [Gauge](https://playbook.powerapp.cloud/kits/gauge/react) | [highcharts](https://www.npmjs.com/package/highcharts),
[highcharts-react-official](https://www.npmjs.com/package/highcharts-react-official) | highcharts,
highcharts-react-official | +| **Highlight** | [Highlight](https://playbook.powerapp.cloud/kits/highlight/react) | [react-highlight-words](https://www.npmjs.com/package/react-highlight-words) | react-highlight-words | +| **LightBox** | [LightBox](https://playbook.powerapp.cloud/kits/lightbox/react) | [react-zoom-pan-pinch](https://www.npmjs.com/package/react-zoom-pan-pinch) | react-zoom-pan-pinch | +| **Line Graph** | [Line Graph](https://playbook.powerapp.cloud/kits/line_graph/react) | [highcharts](https://www.npmjs.com/package/highcharts),
[highcharts-react-official](https://www.npmjs.com/package/highcharts-react-official) | highcharts,
highcharts-react-official | +| **Multi Level Select** | [Multi Level Select](https://playbook.powerapp.cloud/kits/multi_level_select/react) | [lodash](https://www.npmjs.com/package/lodash) | lodash | +| **Passphrase** | [Passphrase](https://playbook.powerapp.cloud/kits/passphrase/react) | [react-popper](https://www.npmjs.com/package/react-popper) | react-popper | +| **Phone Number Input** | [Phone Number Input](https://playbook.powerapp.cloud/kits/phone_number_input/react) | [intl-tel-input](https://www.npmjs.com/package/intl-tel-input) | intl-tel-input | +| **Popover** | [Popover](https://playbook.powerapp.cloud/kits/popover/react) | [lodash](https://www.npmjs.com/package/lodash),
[react-popper](https://www.npmjs.com/package/react-popper) | lodash,
react-popper | +| **Rich Text Editor**
(Trix Editor) | [Rich Text Editor](https://playbook.powerapp.cloud/kits/rich_text_editor/react) | [trix](https://www.npmjs.com/package/trix),
[react-trix](https://www.npmjs.com/package/react-trix) | trix,
react-trix | +| **Tooltip** | [Tooltip](https://playbook.powerapp.cloud/kits/tooltip/react) | [@floating-ui/react](https://www.npmjs.com/package/@floating-ui/react) | @floating-ui/react | +| **Treemap Chart** | [Treemap Chart](https://playbook.powerapp.cloud/kits/treemap_chart/react) | [highcharts](https://www.npmjs.com/package/highcharts),
[highcharts-react-official](https://www.npmjs.com/package/highcharts-react-official) | highcharts,
highcharts-react-official | +| **Typeahead** | [Typeahead](https://playbook.powerapp.cloud/kits/typeahead/react) | [react-select](https://www.npmjs.com/package/react-select),
[lodash](https://www.npmjs.com/package/lodash) | react-select,
lodash | +| **Walkthrough** | [Walkthrough](https://playbook.powerapp.cloud/kits/walkthrough/react) | [react-joyride](https://www.npmjs.com/package/react-joyride) | react-joyride | + +## Notes +**Rich Text Editor**: This kit supports two different editors: +**TipTap Editor**: Requires manual installation of `tiptap` and various `@tiptap/*` extensions (listed above under Unbundled Dependencies). +**Trix Editor**: Dependencies (`trix` and `react-trix`) are bundled with the kit; no extra installation is needed. diff --git a/playbook-website/config/initializers/global_variables.rb b/playbook-website/config/initializers/global_variables.rb index 68052286d5..ca695bca59 100644 --- a/playbook-website/config/initializers/global_variables.rb +++ b/playbook-website/config/initializers/global_variables.rb @@ -69,10 +69,11 @@ ], } -# Move HTML figma to the end +# Move these pages to the end of the Getting Started page +page_names = ["HTML&_CSS", "figma_setup", "how_to_theme", "dependencies"] -move_pages = navigation[:getting_started][:pages].select { |page| ["HTML&_CSS", "figma_setup", "how_to_theme"].include?(page[:page_id]) } -navigation[:getting_started][:pages].reject! { |page| ["HTML&_CSS", "figma_setup", "how_to_theme"].include?(page[:page_id]) } +move_pages = navigation[:getting_started][:pages].select { |page| page_names.include?(page[:page_id]) } +navigation[:getting_started][:pages].reject! { |page| page_names.include?(page[:page_id]) } navigation[:getting_started][:pages].concat(move_pages) DOCS = navigation From 3b17776cd4a398985a5069db28992ba231b82c40 Mon Sep 17 00:00:00 2001 From: Gary Kang <42440452+kangaree@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:30:48 -0400 Subject: [PATCH 07/14] [PLAY-1618] Font Awesome Setup Documentation - Phase 1 (#3865) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **What does this PR do?** Add Font Awesome setup docs for Ruby on Rails asset pipeline. [PLAY-1618](https://runway.powerhrg.com/backlog_items/PLAY-1618) **Screenshots:** Screenshots to visualize your addition/change ![Screenshot 2024-10-31 at 8 12 12 AM](https://github.com/user-attachments/assets/70af7291-6f8d-49c0-997c-578281c97790) **How to test?** Steps to confirm the desired behavior: 1. Go to /guides/getting_started 2. Click Font Awesome 3. Review the doc #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. --------- Co-authored-by: Mark Co-authored-by: Mark Rosenberg <38965626+markdoeswork@users.noreply.github.com> --- .../guides/getting_started/font_awesome.md | 73 +++++++++++++++++++ .../config/initializers/global_variables.rb | 2 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 playbook-website/app/views/guides/getting_started/font_awesome.md diff --git a/playbook-website/app/views/guides/getting_started/font_awesome.md b/playbook-website/app/views/guides/getting_started/font_awesome.md new file mode 100644 index 0000000000..732a79757b --- /dev/null +++ b/playbook-website/app/views/guides/getting_started/font_awesome.md @@ -0,0 +1,73 @@ +--- +title: Font Awesome Setup +description: Playbook seamlessly integrates with Font Awesome, a leading icon library known for its extensive collection of high-quality, scalable icons. This integration not only enhances the visual appeal of websites and applications but also improves overall usability. +icon: font-awesome +--- + +Playbook seamlessly integrates with [Font Awesome](https://fontawesome.com/), a leading icon library known for its extensive collection of high-quality, scalable icons. This integration not only enhances the visual appeal of websites and applications but also improves overall usability. + +Some Font Awesome benefits: + +**1. Wide Range of Icons:** Font Awesome offers a vast selection of icons to suit a variety of needs. You can easily find the perfect icon for your project through their [icon search](https://fontawesome.com/search). +**2. Ease of Use:** The icons are straightforward to implement. With just a few lines of code, you can quickly and easily add visual elements to your web projects. Note, a Pro subscription is required for access to a wider range of icons beyond the [Free set](https://fontawesome.com/search?o=r&m=free&s=regular). +**3. Visual Appeal:** Incorporating these icons can improve the aesthetic of your site or application, making it more attractive to users. With Playbook, you have the flexibility to customize color, size, and animations. +**4. User-Friendliness:** Icons can help users navigate and understand your website or application more efficiently, enhancing their overall experience. Font Awesome icons are web fonts compatible with most browsers and are optimized for performance and accessibility. + +Integrating Font Awesome with Playbook ensures that you have access to these benefits, making your projects more polished and professional. + +![fontawesome](https://github.com/user-attachments/assets/638b63ad-56d3-4819-8e05-fcbb175bedc7) + +## Ruby on Rails Setup (default with asset pipeline) + +**Make sure you are on Rails 7 or higher.** + +**1.** Follow the [Ruby on Rails Setup getting started page](/guides/getting_started/ruby_on_rails_setup) to setup Playbook with your Rails project. + +**2.** Setup Pro or Free Font Awesome to use our Icon Component. + +**Pro:** + +```rb +# app/assets/stylesheets/application.scss + @import "font-awesome-pro"; + @import "font-awesome-pro/solid"; + @import "font-awesome-pro/regular"; + @import "playbook"; +``` + +```rb +# app/Gemfile + source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do + gem "font-awesome-pro-sass", "6.2.0" + end +``` + +**Free:** + +*Currently only [Free Regular](https://fontawesome.com/search?o=r&m=free&s=regular) icons are supported in our icon component structure.* + +```rb +# app/assets/stylesheets/application.scss + @import "font-awesome"; +``` + +```rb +# app/Gemfile + source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do + gem "font-awesome-pro-sass", "6.2.0" + end +``` + +**3.** Bundle all the things! + +```sh +bundle install +``` + +**4.** **Go build awesome stuff!** + +Refer to our [Icon kit](/kits/icon) to get started with Font Awesome icons in Playbook. + +```rb +<%= pb_rails("icon", props: { icon: "font-awesome", fixed_width: true }) %> +``` diff --git a/playbook-website/config/initializers/global_variables.rb b/playbook-website/config/initializers/global_variables.rb index ca695bca59..c88293355d 100644 --- a/playbook-website/config/initializers/global_variables.rb +++ b/playbook-website/config/initializers/global_variables.rb @@ -70,7 +70,7 @@ } # Move these pages to the end of the Getting Started page -page_names = ["HTML&_CSS", "figma_setup", "how_to_theme", "dependencies"] +page_names = ["HTML&_CSS", "figma_setup", "how_to_theme", "dependencies", "font_awesome"] move_pages = navigation[:getting_started][:pages].select { |page| page_names.include?(page[:page_id]) } navigation[:getting_started][:pages].reject! { |page| page_names.include?(page[:page_id]) } From 50fc9f6a058a5281c634b342d23e1122b7dac83d Mon Sep 17 00:00:00 2001 From: Elisa Shapiro <83474365+ElisaShapiro@users.noreply.github.com> Date: Tue, 5 Nov 2024 08:59:22 -0500 Subject: [PATCH 08/14] [PBNTR-576] Tooltip for Truncated Form Pills (#3856) **What does this PR do?** A clear and concise description with your runway ticket url. [PBNTR-576](https://runway.powerhrg.com/backlog_items/PBNTR-576) follows up [PBNTR-550](https://runway.powerhrg.com/backlog_items/PBNTR-550) by adding a Tooltip containing text or tag content in full of a truncated Form Pill. This PR does the following: - Adds a Tooltip to the text, tag, or avatar name component of a truncated Form Pill. The Tooltip contains the entirety of the text content (does not include avatar image or icon) - Expands upon PBNTR-550 by adding the truncate prop to Rails Form Pills as well - all Rails Typeaheads With Pills are react-rendered-rails, so this expands the behavior so React/Rails are consistent. (**Note**: there are no solo rails form pills in use in [Nitro](https://github.com/search?q=repo%3Apowerhome%2Fnitro-web+pb_rails%28%22form_pill&type=code)). For Rails Tooltips, an id must be included; if not included, the Form Pill will be truncated but no Tooltip will appear (this is not necessary for React, nor for react-rendered-rails Typeahead). - Updates the Truncated Text Rails and React doc examples to include several form pill variations (an avatar, an icon+text tag, and a text tag only) within a Card of a small width to demonstrate the truncation/tooltip in a non-Typeahead environment. Locally I tested many variations (including truncation on a small Form Pill, or one with customized color). **Screenshots:** Screenshots to visualize your addition/change React Truncated Text doc example screenshots showing Tooltip react doc ex typeahead react doc ex formpill Rails Truncated Text doc example screenshots showing Tooltip for PR rails updated typeahead doc ex for PR rails updated formpill doc ex **How to test?** Steps to confirm the desired behavior: 1. Go to the truncated text doc example ([rails](https://pr3856.playbook.beta.px.powerapp.cloud/kits/form_pill/rails#truncated-text)/[react](https://pr3856.playbook.beta.px.powerapp.cloud/kits/form_pill/react#truncated-text)). 2. Go to the Typeahead in the "Truncation Within Typeahead" section and select a name. Hover over the Form Pill that appears with their truncated name and observe the Tooltip in action displaying their full name. 3. Hover over one/all of the truncated Form Pills in the "Form Pill Truncation" section. Observe the Tooltip in action. For the last Form Pill in the Rails doc ex, note that no Tooltip will appear because it does not have an id. #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. ~~- [ ] **TESTS** I have added test coverage to my code.~~ --- .../playbook/pb_form_pill/_form_pill.scss | 8 ++- .../playbook/pb_form_pill/_form_pill.tsx | 56 ++++++++++--------- .../docs/_form_pill_truncated_text.html.erb | 25 ++++++++- .../docs/_form_pill_truncated_text.jsx | 27 ++++++++- .../docs/_form_pill_truncated_text.md | 1 - .../docs/_form_pill_truncated_text_rails.md | 3 + .../docs/_form_pill_truncated_text_react.md | 1 + .../playbook/pb_form_pill/form_pill.html.erb | 54 +++++++++++++++--- 8 files changed, 135 insertions(+), 40 deletions(-) delete mode 100644 playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md create mode 100644 playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_rails.md create mode 100644 playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_react.md 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 87f00ef302..2541ef05f8 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 @@ -142,7 +142,9 @@ $form_pill_colors: map-merge($status_color_text, map-merge($data_colors, $produc height: 12px !important; width: 12px !important; padding-right: $space_xs; - + .pb_form_pill_text, + .pb_form_pill_tag { + + .pb_form_pill_text, + .pb_form_pill_tag, + + .pb_tooltip_kit .pb_form_pill_text, + .pb_tooltip_kit .pb_form_pill_tag, + + div .pb_form_pill_text, + div .pb_form_pill_tag { padding-left: 0; } } @@ -169,7 +171,9 @@ $form_pill_colors: map-merge($status_color_text, map-merge($data_colors, $produc } .pb_form_pill_icon { padding-right: $space_xxs; - + .pb_form_pill_text, + .pb_form_pill_tag { + + .pb_form_pill_text, + .pb_form_pill_tag, + + .pb_tooltip_kit .pb_form_pill_text, + .pb_tooltip_kit .pb_form_pill_tag, + + div .pb_form_pill_text, + div .pb_form_pill_tag { padding-left: 0; } } 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 9cbff71a4c..0ea0f7d02b 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 @@ -3,6 +3,7 @@ import classnames from 'classnames' import Title from '../pb_title/_title' import Icon from '../pb_icon/_icon' import Avatar from '../pb_avatar/_avatar' +import Tooltip from '../pb_tooltip/_tooltip' import { globalProps, GlobalProps } from '../utilities/globalProps' import { buildDataProps, buildHtmlProps } from '../utilities/props' @@ -62,6 +63,30 @@ const FormPill = (props: FormPillProps): React.ReactElement => { const dataProps = buildDataProps(data) const htmlProps = buildHtmlProps(htmlOptions) + const renderTitle = (content: string, className: string) => { + const titleComponent = ( + + ) + if (props.truncate) { + return ( + <Tooltip + interaction + placement="top" + position="fixed" + text={content} + > + {titleComponent} + </Tooltip> + ) + } + return titleComponent + } + return ( <div className={css} id={id} @@ -77,12 +102,7 @@ const FormPill = (props: FormPillProps): React.ReactElement => { size="xxs" status={null} /> - <Title - className="pb_form_pill_text" - size={4} - text={name} - truncate={props.truncate} - /> + {renderTitle(name, "pb_form_pill_text")} </> )} {((name && icon && !text) || (name && icon && text)) && ( @@ -93,12 +113,7 @@ const FormPill = (props: FormPillProps): React.ReactElement => { size="xxs" status={null} /> - <Title - className="pb_form_pill_text" - size={4} - text={name} - truncate={props.truncate} - /> + {renderTitle(name, "pb_form_pill_text")} <Icon className="pb_form_pill_icon" color={color} @@ -113,22 +128,10 @@ const FormPill = (props: FormPillProps): React.ReactElement => { color={color} icon={icon} /> - <Title - className="pb_form_pill_tag" - size={4} - text={text} - truncate={props.truncate} - /> + {renderTitle(text, "pb_form_pill_tag")} </> )} - {(!name && !icon && text) && ( - <Title - className="pb_form_pill_tag" - size={4} - text={text} - truncate={props.truncate} - /> - )} + {(!name && !icon && text) && renderTitle(text, "pb_form_pill_tag")} <div className="pb_form_pill_close" onClick={onClick} @@ -143,4 +146,5 @@ const FormPill = (props: FormPillProps): React.ReactElement => { </div> ) } + export default FormPill diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb index 3e66bb7446..a298f19a09 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb @@ -13,7 +13,30 @@ id: "typeahead-form-pill", is_multi: true, options: names, - label: "Names", + label: "Truncation Within Typeahead", pills: true, truncate: 1, }) %> + +<%= pb_rails("caption", props: { text: "Form Pill Truncation" }) %> +<%= pb_rails("card", props: { max_width: "xs" }) do %> + <%= pb_rails("form_pill", props: { + name: "Princess Amelia Mignonette Grimaldi Thermopolis Renaldo", + avatar_url: "https://randomuser.me/api/portraits/women/44.jpg", + tabindex: 0, + truncate: 1, + id: "truncation-1" + }) %> + <%= pb_rails("form_pill", props: { + icon: "badge-check", + text: "icon and a very long tag to show truncation", + tabindex: 0, + truncate: 1, + id: "truncation-2" + }) %> + <%= pb_rails("form_pill", props: { + text: "form pill long tag no tooltip show truncation", + tabindex: 0, + truncate: 1, + }) %> +<% end %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx index b599017782..51a8a3e2a5 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx @@ -1,5 +1,5 @@ import React from 'react' -import Typeahead from '../../pb_typeahead/_typeahead' +import { Card, Caption, FormPill, Typeahead } from 'playbook-ui' const names = [ { label: 'Alexander Nathaniel Montgomery', value: 'Alexander Nathaniel Montgomery' }, @@ -15,11 +15,34 @@ const FormPillTruncatedText = (props) => { <Typeahead htmlOptions={{ style: { maxWidth: "240px" }}} isMulti - label="Names" + label="Truncation Within Typeahead" options={names} truncate={1} {...props} /> + <Caption text="Form Pill Truncation"/> + <Card maxWidth="xs"> + <FormPill + avatarUrl="https://randomuser.me/api/portraits/women/44.jpg" + name="Princess Amelia Mignonette Grimaldi Thermopolis Renaldo" + onClick={() => alert('Click!')} + tabIndex={0} + truncate={1} + /> + <FormPill + icon="badge-check" + onClick={() => {alert('Click!')}} + tabIndex={0} + text="icon and a very long tag to show truncation" + truncate={1} + /> + <FormPill + onClick={() => {alert('Click!')}} + tabIndex={0} + text="form pill with a very long tag to show truncation" + truncate={1} + /> + </Card> </> ) } diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md deleted file mode 100644 index e960dc2deb..0000000000 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md +++ /dev/null @@ -1 +0,0 @@ -For pills with longer text, the `truncate` global prop can be used to truncate the label within each Form Pill. See [here](https://playbook.powerapp.cloud/visual_guidelines/truncate) for more information on the truncate global prop. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_rails.md b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_rails.md new file mode 100644 index 0000000000..fc0593464e --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_rails.md @@ -0,0 +1,3 @@ +For Form Pills with longer text, the truncate global prop can be used to truncate the label within each Form Pill. See [here](https://playbook.powerapp.cloud/visual_guidelines/truncate) for more information on the truncate global prop. + +__NOTE__: For Rails Form Pills (not ones embedded within a React-rendered Typeahead or MultiLevelSelect), a unique `id` is required to enable the Tooltip functionality displaying the text or tag section of the Form Pill. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_react.md b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_react.md new file mode 100644 index 0000000000..5b35756d8c --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_react.md @@ -0,0 +1 @@ +For Form Pills with longer text, the `truncate` global prop can be used to truncate the label within each Form Pill. Hover over the truncated Form Pill and a Tooltip containing the text or tag section of the Form Pill will appear. See [here](https://playbook.powerapp.cloud/visual_guidelines/truncate) for more information on the truncate global prop. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb b/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb index b9ddf68b86..4e42ddabb6 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb +++ b/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb @@ -1,19 +1,57 @@ <%= content_tag(:div, id: object.id, data: object.data, class: object.classname + object.size_class, tabindex: object.tabindex, **combined_html_options) do %> <% if object.name.present? %> <%= pb_rails("avatar", props: { name: object.name, image_url: object.avatar_url, size: "xxs" }) %> - <%= pb_rails("title", props: { text: object.name, size: 4, classname: "pb_form_pill_text" }) %> + <% if object.truncate %> + <div> + <%= pb_rails("title", props: { + classname: "pb_form_pill_text truncate_#{object.truncate}", + id: object.id, + size: 4, + text: object.name, + }) %> + <% if object.id.present? %> + <%= pb_rails("tooltip", props: { + position: "top", + tooltip_id: "tooltip-#{object.id}", + trigger_element_selector: "##{object.id}" + }) do %> + <%= object.name %> + <% end %> + <% end %> + </div> + <% else %> + <%= pb_rails("title", props: { classname: "pb_form_pill_text", id: object.id, size: 4, text: object.name }) %> + <% end %> <% if object.icon.present? %> <%= pb_rails("icon", props: { classname: "pb_form_pill_icon", color: object.color, icon: object.icon }) %> <% end %> <% elsif object.text.present? %> - <% if object.icon.present? %> - <%= pb_rails("icon", props: { classname: "pb_form_pill_icon", color: object.color, icon: object.icon }) %> - <% end %> - <% if object.text.present? %> - <%= pb_rails("title", props: { text: object.text, size: 4, classname: "pb_form_pill_tag" }) %> - <% end %> + <% if object.icon.present? %> + <%= pb_rails("icon", props: { classname: "pb_form_pill_icon", color: object.color, icon: object.icon }) %> + <% end %> + <% if object.truncate %> + <div> + <%= pb_rails("title", props: { + classname: "pb_form_pill_tag truncate_#{object.truncate}", + id: object.id, + size: 4, + text: object.text, + }) %> + <% if object.id.present? %> + <%= pb_rails("tooltip", props: { + position: "top", + tooltip_id: "tooltip-#{object.id}", + trigger_element_selector: "##{object.id}" + }) do %> + <%= object.text %> + <% end %> + <% end %> + </div> + <% else %> + <%= pb_rails("title", props: { classname: "pb_form_pill_tag", id: object.id, size: 4, text: object.text, }) %> + <% end %> <% end %> <%= pb_rails("body", props: { classname: "pb_form_pill_close" }) do %> <%= pb_rails("icon", props: { icon: 'times', fixed_width: true, size: object.close_icon_size }) %> <% end %> -<% end %> +<% end %> \ No newline at end of file From c5f4170098a8f927b913befa70b5817bbb8ae4ea Mon Sep 17 00:00:00 2001 From: Mark Rosenberg <38965626+markdoeswork@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:01:53 -0500 Subject: [PATCH 09/14] [PLAY-1586] Timeline Sub Sub Components (#3801) **What does this PR do?** A clear and concise description with your runway ticket url. Runway https://runway.powerhrg.com/backlog_items/PLAY-1586 In this story we let devs add children or "what ever kit they want" to the date and node area of the timeline kit The major changes to react is in the `playbook/app/pb_kits/playbook/pb_timeline/_item.tsx` and in rails i added a new pattern to be able to conditionally render fragments of content I have an alpha here https://github.com/powerhome/nitro-web/pull/43266 **Screenshots:** Screenshots to visualize your addition/change ![screenshot-127_0_0_1_3000-2024_10_17-09_57_21](https://github.com/user-attachments/assets/b07c56ec-9c52-4b1b-8d36-7971e76d3de8) **How to test?** Steps to confirm the desired behavior: 1. Go to https://pr3801.playbook.beta.px.powerapp.cloud/kits/timeline/react#with-children 2. Witness greatness #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. - [x] **TESTS** I have added test coverage to my code. --------- Co-authored-by: Jasper Furniss <jasper.furniss@powerhrg.com> --- .../pb_kits/playbook/pb_timeline/_item.tsx | 82 +++++++++++++----- .../playbook/pb_timeline/_timeline.tsx | 8 ++ .../playbook/pb_timeline/detail.html.erb | 3 + .../pb_kits/playbook/pb_timeline/detail.rb | 11 +++ .../docs/_timeline_with_children.html.erb | 43 ++++++++++ .../docs/_timeline_with_children.jsx | 68 +++++++++++++++ .../docs/_timeline_with_children.md | 2 + .../playbook/pb_timeline/docs/example.yml | 3 +- .../playbook/pb_timeline/docs/index.js | 1 + .../playbook/pb_timeline/item.html.erb | 38 ++++----- .../app/pb_kits/playbook/pb_timeline/item.rb | 4 + .../playbook/pb_timeline/label.html.erb | 12 +++ .../app/pb_kits/playbook/pb_timeline/label.rb | 13 +++ .../playbook/pb_timeline/step.html.erb | 14 ++++ .../app/pb_kits/playbook/pb_timeline/step.rb | 16 ++++ .../pb_timeline/subcomponents/Detail.tsx | 29 +++++++ .../pb_timeline/subcomponents/Label.tsx | 38 +++++++++ .../pb_timeline/subcomponents/Step.tsx | 42 ++++++++++ .../pb_timeline/subcomponents/index.tsx | 3 + .../playbook/pb_timeline/timeline.test.js | 84 +++++++++++++++++++ .../playbook/kits/timeline_detail_spec.rb | 13 +++ .../playbook/kits/timeline_label_spec.rb | 15 ++++ .../playbook/kits/timeline_step_spec.rb | 21 +++++ 23 files changed, 518 insertions(+), 45 deletions(-) create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/detail.html.erb create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/detail.rb create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.html.erb create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.jsx create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.md create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/label.html.erb create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/label.rb create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/step.html.erb create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/step.rb create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Detail.tsx create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Step.tsx create mode 100644 playbook/app/pb_kits/playbook/pb_timeline/subcomponents/index.tsx create mode 100644 playbook/spec/pb_kits/playbook/kits/timeline_detail_spec.rb create mode 100644 playbook/spec/pb_kits/playbook/kits/timeline_label_spec.rb create mode 100644 playbook/spec/pb_kits/playbook/kits/timeline_step_spec.rb diff --git a/playbook/app/pb_kits/playbook/pb_timeline/_item.tsx b/playbook/app/pb_kits/playbook/pb_timeline/_item.tsx index 3d6ef9eafd..f5aa0d6d2b 100644 --- a/playbook/app/pb_kits/playbook/pb_timeline/_item.tsx +++ b/playbook/app/pb_kits/playbook/pb_timeline/_item.tsx @@ -1,12 +1,15 @@ import React from 'react' import classnames from 'classnames' - import { buildCss, buildHtmlProps } from '../utilities/props' -import { globalProps, GlobalProps } from "../utilities/globalProps"; +import { globalProps, GlobalProps } from "../utilities/globalProps" import DateStacked from '../pb_date_stacked/_date_stacked' import IconCircle from '../pb_icon_circle/_icon_circle' +import TimelineLabel from './subcomponents/Label' +import TimelineStep from './subcomponents/Step' +import TimelineDetail from './subcomponents/Detail' + type ItemProps = { className?: string, children?: React.ReactNode[] | React.ReactNode, @@ -17,6 +20,13 @@ type ItemProps = { lineStyle?: 'solid' | 'dotted', } & GlobalProps +function isElementOfType<P>( + element: React.ReactNode, + component: React.ComponentType<P> +): element is React.ReactElement<P> { + return React.isValidElement<P>(element) && element.type === component +} + const TimelineItem = ({ className, children, @@ -31,31 +41,57 @@ const TimelineItem = ({ const htmlProps = buildHtmlProps(htmlOptions) + const childrenArray = React.Children.toArray(children) + + const labelChild = childrenArray.find( + (child): child is React.ReactElement => isElementOfType(child, TimelineLabel) + ) + + const stepChild = childrenArray.find( + (child): child is React.ReactElement => isElementOfType(child, TimelineStep) + ) + + const detailChild = childrenArray.find( + (child): child is React.ReactElement => isElementOfType(child, TimelineDetail) + ) + + const otherChildren = childrenArray.filter( + (child) => + !isElementOfType(child, TimelineLabel) && + !isElementOfType(child, TimelineStep) && + !isElementOfType(child, TimelineDetail) + ) + return ( - <div + <div {...htmlProps} className={classnames(timelineItemCss, globalProps(props), className)} > - <div className="pb_timeline_item_left_block"> - {date && - <DateStacked - align="center" - date={date} - size="sm" - /> - } - </div> - <div className="pb_timeline_item_step"> - <IconCircle - icon={icon} - size="xs" - variant={iconColor} - /> - <div className="pb_timeline_item_connector" /> - </div> - <div className="pb_timeline_item_right_block"> - {children} - </div> + {labelChild || ( + <div className="pb_timeline_item_left_block"> + {date && ( + <DateStacked + align="center" + date={date} + size="sm" + /> + )} + </div> + )} + {stepChild || ( + <div className="pb_timeline_item_step"> + <IconCircle icon={icon} + size="xs" + variant={iconColor} + /> + <div className="pb_timeline_item_connector" /> + </div> + )} + {detailChild || ( + <div className="pb_timeline_item_right_block"> + { otherChildren } + </div> + )} </div> ) } diff --git a/playbook/app/pb_kits/playbook/pb_timeline/_timeline.tsx b/playbook/app/pb_kits/playbook/pb_timeline/_timeline.tsx index e58d9f8acb..a0e3f53af6 100644 --- a/playbook/app/pb_kits/playbook/pb_timeline/_timeline.tsx +++ b/playbook/app/pb_kits/playbook/pb_timeline/_timeline.tsx @@ -5,6 +5,11 @@ import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../uti import { GlobalProps, globalProps } from '../utilities/globalProps' import TimelineItem from './_item' +import { + TimelineStep, + TimelineLabel, + TimelineDetail, +} from './subcomponents' type TimelineProps = { aria?: { [key: string]: string }, @@ -47,5 +52,8 @@ const Timeline = ({ } Timeline.Item = TimelineItem +Timeline.Step = TimelineStep +Timeline.Label = TimelineLabel +Timeline.Detail = TimelineDetail export default Timeline diff --git a/playbook/app/pb_kits/playbook/pb_timeline/detail.html.erb b/playbook/app/pb_kits/playbook/pb_timeline/detail.html.erb new file mode 100644 index 0000000000..919544cc91 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/detail.html.erb @@ -0,0 +1,3 @@ +<%= pb_content_tag do %> + <%= content.presence %> +<% end %> diff --git a/playbook/app/pb_kits/playbook/pb_timeline/detail.rb b/playbook/app/pb_kits/playbook/pb_timeline/detail.rb new file mode 100644 index 0000000000..810248dae4 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/detail.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Playbook + module PbTimeline + class Detail < Playbook::KitBase + def classname + generate_classname("pb_timeline_item_right_block") + end + end + end +end diff --git a/playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.html.erb b/playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.html.erb new file mode 100644 index 0000000000..1725989c17 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.html.erb @@ -0,0 +1,43 @@ +<%= pb_rails("timeline", props: {orientation: "horizontal", show_date: true}) do %> + <%= pb_rails("timeline/item", props: { line_style: "solid"}) do |item| %> + + <% item.label do %> + <%= pb_rails("timeline/label") do %> + <%= pb_rails("title", props: { text: "Any Kit Here", size: 2 }) %> + <% end %> + <% end %> + + <% item.step do %> + <%= pb_rails("timeline/step", props: { icon: 'check', icon_color: 'teal' }) %> + <% end %> + + <% item.detail do %> + <%= pb_rails("title_detail", props: { + title: "Jackson Heights", + detail: "37-27 74th Street" + }) %> + <% end %> + <% end %> + <%= pb_rails("timeline/item", props: { line_style: "dotted"}) do |item| %> + + <% item.step do %> + <%= pb_rails("timeline/step") do %> + <%= pb_rails("pill", props: { text: "Any Kit" , variant: "success" }) %> + <% end %> + <% end %> + + <% item.detail do %> + <%= pb_rails("title_detail", props: { + title: "Greenpoint", + detail: "81 Gate St Brooklyn" + }) %> + <% end %> + <% end %> + + <%= pb_rails("timeline/item", props: {icon: "map-marker-alt", icon_color: "purple", date: Date.today+1 }) do |item| %> + <%= pb_rails("title_detail", props: { + title: "Society Hill", + detail: "72 E St Astoria" + }) %> + <% end %> +<% end %> diff --git a/playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.jsx b/playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.jsx new file mode 100644 index 0000000000..c426fbc066 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.jsx @@ -0,0 +1,68 @@ +import React from 'react' + +import Timeline from '../_timeline' +import Title from '../../pb_title/_title' +import Pill from '../../pb_pill/_pill' + +import TitleDetail from '../../pb_title_detail/_title_detail' + +const TimelineWithChildren = (props) => ( + <div> + <Timeline orientation="horizontal" + showDate + {...props} + > + <Timeline.Item lineStyle="solid" + {...props} + > + <Timeline.Label> + <Title size={2} + text='Any Kit Here' + /> + </Timeline.Label> + <Timeline.Step icon="user" + iconColor="royal" + /> + <Timeline.Detail> + <TitleDetail detail="37-27 74th Street" + title="Jackson Heights" + {...props} + /> + </Timeline.Detail> + </Timeline.Item> + + <Timeline.Item lineStyle="dotted" + {...props} + > + <Timeline.Step> + <Pill text="Any Kit" + variant="success" + /> + </Timeline.Step> + <Timeline.Detail> + <TitleDetail detail="81 Gate St Brooklyn" + title="Greenpoint" + {...props} + /> + </Timeline.Detail> + </Timeline.Item> + + <Timeline.Item lineStyle="solid" + {...props} + > + <Timeline.Label date={new Date(new Date().setDate(new Date().getDate() + 1))} /> + <Timeline.Step icon="map-marker-alt" + iconColor="purple" + /> + <Timeline.Detail> + <TitleDetail detail="72 E St Astoria" + title="Society Hill" + {...props} + /> + </Timeline.Detail> + </Timeline.Item> + </Timeline> + </div> +) + +export default TimelineWithChildren diff --git a/playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.md b/playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.md new file mode 100644 index 0000000000..dc0264dd9e --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.md @@ -0,0 +1,2 @@ +Any kit can be used inside of our compound components of label, step, or detail. Expand the code snippet below to see how to use these children elements. + diff --git a/playbook/app/pb_kits/playbook/pb_timeline/docs/example.yml b/playbook/app/pb_kits/playbook/pb_timeline/docs/example.yml index 2cf2a99a73..1cd961b5d5 100644 --- a/playbook/app/pb_kits/playbook/pb_timeline/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_timeline/docs/example.yml @@ -4,10 +4,11 @@ examples: - timeline_default: Default - timeline_vertical: Vertical - timeline_with_date: With Date + - timeline_with_children: With Children react: - timeline_default: Default - timeline_vertical: Vertical - timeline_with_date: With Date - + - timeline_with_children: With Children diff --git a/playbook/app/pb_kits/playbook/pb_timeline/docs/index.js b/playbook/app/pb_kits/playbook/pb_timeline/docs/index.js index 35398d22d6..8da0d1e1f0 100644 --- a/playbook/app/pb_kits/playbook/pb_timeline/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_timeline/docs/index.js @@ -1,3 +1,4 @@ export { default as TimelineDefault } from './_timeline_default.jsx' export { default as TimelineVertical } from './_timeline_vertical.jsx' export { default as TimelineWithDate } from './_timeline_with_date.jsx' +export { default as TimelineWithChildren } from './_timeline_with_children.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_timeline/item.html.erb b/playbook/app/pb_kits/playbook/pb_timeline/item.html.erb index cb815cb6e2..8f7153b22b 100644 --- a/playbook/app/pb_kits/playbook/pb_timeline/item.html.erb +++ b/playbook/app/pb_kits/playbook/pb_timeline/item.html.erb @@ -1,25 +1,21 @@ <%= pb_content_tag do %> + <% if label %> + <%= label %> + <% else %> + <%= pb_rails("timeline/label", props: { date: date }) %> + <% end %> - <div class="pb_timeline_item_left_block"> - <% if object.date.present? %> - <%= pb_rails("date_stacked", props: { - date: object.date, - size: "sm", - align: "center" - }) %> - <% end %> - </div> - - <div class="pb_timeline_item_step"> - <%= pb_rails("icon_circle", props: { - icon: object.icon, - variant: object.icon_color, - size: "xs" - }) %> - <div class="pb_timeline_item_connector"></div> - </div> + <% if step %> + <%= step %> + <% else %> + <%= pb_rails("timeline/step", props: { icon: icon, icon_color: icon_color }) %> + <% end %> - <div class="pb_timeline_item_right_block"> - <%= content.presence %> - </div> + <% if detail%> + <%= detail%> + <% else %> + <%= pb_rails("timeline/detail") do %> + <%= content %> + <% end %> + <% end %> <% end %> diff --git a/playbook/app/pb_kits/playbook/pb_timeline/item.rb b/playbook/app/pb_kits/playbook/pb_timeline/item.rb index f5c5830777..9a954cc413 100644 --- a/playbook/app/pb_kits/playbook/pb_timeline/item.rb +++ b/playbook/app/pb_kits/playbook/pb_timeline/item.rb @@ -13,6 +13,10 @@ class Item < Playbook::KitBase values: %w[solid dotted], default: "solid" + renders_one :label + renders_one :step + renders_one :detail + def classname generate_classname("pb_timeline_item_kit", line_style) end diff --git a/playbook/app/pb_kits/playbook/pb_timeline/label.html.erb b/playbook/app/pb_kits/playbook/pb_timeline/label.html.erb new file mode 100644 index 0000000000..c7b4b3b18b --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/label.html.erb @@ -0,0 +1,12 @@ +<%= pb_content_tag do %> + <% if object.date.present? %> + <%= pb_rails("date_stacked", props: { + date: object.date, + size: "sm", + align: "center" + }) %> + <% else %> + <%= content.presence %> + <% end %> +<% end %> + diff --git a/playbook/app/pb_kits/playbook/pb_timeline/label.rb b/playbook/app/pb_kits/playbook/pb_timeline/label.rb new file mode 100644 index 0000000000..bfb74a469f --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/label.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Playbook + module PbTimeline + class Label < Playbook::KitBase + prop :date + + def classname + generate_classname("pb_timeline_item_left_block") + end + end + end +end diff --git a/playbook/app/pb_kits/playbook/pb_timeline/step.html.erb b/playbook/app/pb_kits/playbook/pb_timeline/step.html.erb new file mode 100644 index 0000000000..533e1cb383 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/step.html.erb @@ -0,0 +1,14 @@ +<%= pb_content_tag do %> + <% if object.icon.present? %> + <%= pb_rails("icon_circle", props: { + icon: object.icon, + variant: object.icon_color, + size: "xs" + }) %> + <% else %> + <%= content.presence %> + <% end %> + <div class="pb_timeline_item_connector"></div> +<% end %> + + diff --git a/playbook/app/pb_kits/playbook/pb_timeline/step.rb b/playbook/app/pb_kits/playbook/pb_timeline/step.rb new file mode 100644 index 0000000000..a80d28d51a --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/step.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Playbook + module PbTimeline + class Step < Playbook::KitBase + prop :icon, type: Playbook::Props::String + prop :icon_color, type: Playbook::Props::Enum, + values: %w[default royal blue purple teal red yellow green], + default: "default" + + def classname + generate_classname("pb_timeline_item_step") + end + end + end +end diff --git a/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Detail.tsx b/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Detail.tsx new file mode 100644 index 0000000000..0365376132 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Detail.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import classnames from 'classnames' +import { buildHtmlProps } from '../../utilities/props' +import { globalProps, GlobalProps } from "../../utilities/globalProps" + +type TimelineDetailProps = { + children?: React.ReactNode, + className?: string, + htmlOptions?: { [key: string]: any }, +} & GlobalProps + +const TimelineDetail: React.FC<TimelineDetailProps> = ({ + children, + className, + htmlOptions = {}, + ...props +}) => { + const htmlProps = buildHtmlProps(htmlOptions) + return ( + <div + {...htmlProps} + className={classnames('pb_timeline_item_right_block', globalProps(props), className)} + > + {children} + </div> + ) +} + +export default TimelineDetail diff --git a/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx b/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx new file mode 100644 index 0000000000..717e92aeaf --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import classnames from 'classnames' +import { buildHtmlProps } from '../../utilities/props' +import { globalProps, GlobalProps } from "../../utilities/globalProps" +import DateStacked from '../../pb_date_stacked/_date_stacked' + +type TimelineLabelProps = { + date?: Date, + children?: React.ReactNode, + className?: string, + htmlOptions?: { [key: string]: any }, +} & GlobalProps + +const TimelineLabel: React.FC<TimelineLabelProps> = ({ + date, + children, + className, + htmlOptions = {}, + ...props +}) => { + const htmlProps = buildHtmlProps(htmlOptions) + return ( + <div + {...htmlProps} + className={classnames('pb_timeline_item_left_block', globalProps(props), className)} + > + {children} + {date && ( + <DateStacked align="center" + date={date} + size="sm" + /> + )} + </div> + ) +} + +export default TimelineLabel diff --git a/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Step.tsx b/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Step.tsx new file mode 100644 index 0000000000..648c1d9b0a --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/Step.tsx @@ -0,0 +1,42 @@ +import React from 'react' +import classnames from 'classnames' +import { buildHtmlProps } from '../../utilities/props' +import { globalProps, GlobalProps } from "../../utilities/globalProps" +import IconCircle from '../../pb_icon_circle/_icon_circle' + +type TimelineStepProps = { + icon?: string, + iconColor?: 'default' | 'royal' | 'blue' | 'purple' | 'teal' | 'red' | 'yellow' | 'green', + children?: React.ReactNode, + className?: string, + htmlOptions?: { [key: string]: any }, +} & GlobalProps + +const TimelineStep: React.FC<TimelineStepProps> = ({ + icon = 'user', + iconColor = 'default', + children, + className, + htmlOptions = {}, + ...props +}) => { + const htmlProps = buildHtmlProps(htmlOptions) + return ( + <div + {...htmlProps} + className={classnames('pb_timeline_item_step', globalProps(props), className)} + > + {children ? ( + children + ) : ( + <IconCircle icon={icon} + size="xs" + variant={iconColor} + /> + )} + <div className="pb_timeline_item_connector" /> + </div> + ) +} + +export default TimelineStep diff --git a/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/index.tsx b/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/index.tsx new file mode 100644 index 0000000000..693e027a7d --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_timeline/subcomponents/index.tsx @@ -0,0 +1,3 @@ +export { default as TimelineLabel } from './Label'; +export { default as TimelineDetail } from './Detail'; +export { default as TimelineStep } from './Step'; diff --git a/playbook/app/pb_kits/playbook/pb_timeline/timeline.test.js b/playbook/app/pb_kits/playbook/pb_timeline/timeline.test.js index fa71db5bad..9468e2070d 100644 --- a/playbook/app/pb_kits/playbook/pb_timeline/timeline.test.js +++ b/playbook/app/pb_kits/playbook/pb_timeline/timeline.test.js @@ -2,6 +2,10 @@ import React from 'react' import { render, screen } from '../utilities/test-utils' import Timeline from './_timeline' +import TimelineItem from './_item' +import TimelineLabel from './subcomponents/Label' +import TimelineStep from './subcomponents/Step' +import TimelineDetail from './subcomponents/Detail' import TitleDetail from '../pb_title_detail/_title_detail' const testId = 'timeline' @@ -43,18 +47,91 @@ const TimelineDefault = (props) => ( </> ) +const TimelineWithChildren = (props) => ( + <> + <Timeline + className={className} + data={{ testid: testId }} + orientation="horizontal" + showDate + {...props} + > + <TimelineItem lineStyle="solid" + {...props} + > + <TimelineLabel date={new Date()} /> + <TimelineStep icon="user" + iconColor="royal" + /> + <TimelineDetail> + <TitleDetail + detail="37-27 74th Street" + title="Jackson Heights" + {...props} + /> + </TimelineDetail> + </TimelineItem> + + <TimelineItem lineStyle="dotted" + {...props} + > + <TimelineStep icon="check" + iconColor="teal" + /> + <TimelineDetail> + <TitleDetail + detail="81 Gate St Brooklyn" + title="Greenpoint" + {...props} + /> + </TimelineDetail> + </TimelineItem> + + <TimelineItem lineStyle="solid" + {...props} + > + <TimelineLabel + date={new Date(new Date().setDate(new Date().getDate() + 1))} + /> + <TimelineStep icon="map-marker-alt" + iconColor="purple" + /> + <TimelineDetail> + <TitleDetail + detail="72 E St Astoria" + title="Society Hill" + {...props} + /> + </TimelineDetail> + </TimelineItem> + </Timeline> + </> +) + test('should pass data prop', () => { render(<TimelineDefault />) const kit = screen.getByTestId(testId) expect(kit).toBeInTheDocument() }) +test('should pass data prop using children', () => { + render(<TimelineWithChildren />) + const kit = screen.getByTestId(testId) + expect(kit).toBeInTheDocument() +}) + test('should pass className prop', () => { render(<TimelineDefault />) const kit = screen.getByTestId(testId) expect(kit).toHaveClass(className) }) +test('should pass className prop with children', () => { + render(<TimelineWithChildren />) + const kit = screen.getByTestId(testId) + expect(kit).toHaveClass(className) +}) + test('should pass aria prop', () => { render(<TimelineDefault />) const kit = screen.getByTestId(testId) @@ -86,3 +163,10 @@ test('should pass showDate prop', () => { const kit = screen.getByTestId(testId) expect(kit).toHaveClass('pb_timeline_kit__horizontal__with_date') }) + +test('should pass showDate prop with Children', () => { + const props = { showDate: true } + render(<TimelineWithChildren {...props} />) + const kit = screen.getByTestId(testId) + expect(kit).toHaveClass('pb_timeline_kit__horizontal__with_date') +}) diff --git a/playbook/spec/pb_kits/playbook/kits/timeline_detail_spec.rb b/playbook/spec/pb_kits/playbook/kits/timeline_detail_spec.rb new file mode 100644 index 0000000000..fc77b24b5c --- /dev/null +++ b/playbook/spec/pb_kits/playbook/kits/timeline_detail_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require_relative "../../../../app/pb_kits/playbook/pb_timeline/detail" + +RSpec.describe Playbook::PbTimeline::Detail do + subject { Playbook::PbTimeline::Detail } + + describe "#classname" do + it "returns the correct class name" do + expect(subject.new({}).classname).to eq "pb_timeline_item_right_block" + end + end +end diff --git a/playbook/spec/pb_kits/playbook/kits/timeline_label_spec.rb b/playbook/spec/pb_kits/playbook/kits/timeline_label_spec.rb new file mode 100644 index 0000000000..555837360d --- /dev/null +++ b/playbook/spec/pb_kits/playbook/kits/timeline_label_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require_relative "../../../../app/pb_kits/playbook/pb_timeline/label" + +RSpec.describe Playbook::PbTimeline::Label do + subject { Playbook::PbTimeline::Label } + + it { is_expected.to define_prop(:date) } + + describe "#classname" do + it "returns the correct class name" do + expect(subject.new.classname).to eq "pb_timeline_item_left_block" + end + end +end diff --git a/playbook/spec/pb_kits/playbook/kits/timeline_step_spec.rb b/playbook/spec/pb_kits/playbook/kits/timeline_step_spec.rb new file mode 100644 index 0000000000..ecbe365c72 --- /dev/null +++ b/playbook/spec/pb_kits/playbook/kits/timeline_step_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "../../../../app/pb_kits/playbook/pb_timeline/step" + +RSpec.describe Playbook::PbTimeline::Step do + subject { Playbook::PbTimeline::Step } + + it { is_expected.to define_prop(:icon) } + + it { + is_expected.to define_enum_prop(:icon_color) + .with_default("default") + .with_values("default", "royal", "blue", "purple", "teal", "red", "yellow", "green") + } + + describe "#classname" do + it "returns the correct class name" do + expect(subject.new.classname).to eq "pb_timeline_item_step" + end + end +end From e1b0a8539257d1a4a0ab8864da3884b16de576e2 Mon Sep 17 00:00:00 2001 From: Mark Rosenberg <38965626+markdoeswork@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:18:05 -0500 Subject: [PATCH 10/14] [PLAY-1610] Add details to dependencies docs (#3885) **What does this PR do?** A clear and concise description with your runway ticket url. Runway https://runway.powerhrg.com/backlog_items/PLAY-1610 Added docs to explain the required dependancies to run playbook ui https://pr3885.playbook.beta.px.powerapp.cloud/guides/getting_started/dependencies --------- Co-authored-by: Jasper Furniss <jasper.furniss@powerhrg.com> --- .../views/guides/getting_started/dependencies.md | 15 +++++++++++++++ .../views/guides/getting_started/react_setup.md | 9 +++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/playbook-website/app/views/guides/getting_started/dependencies.md b/playbook-website/app/views/guides/getting_started/dependencies.md index 3323271988..66550e3e03 100644 --- a/playbook-website/app/views/guides/getting_started/dependencies.md +++ b/playbook-website/app/views/guides/getting_started/dependencies.md @@ -4,6 +4,21 @@ icon: code description: Some of our kits require additional libraries to run properly. --- +## Playbook UI Dependencies | React + +Playbook UI's React library needs the following packages installed in your project to work properly: + +```json +"react" + "react-dom" + "react-is" + "react-trix" +``` + +## Playbook UI Dependencies | Rails + +Playbook UI's Rails gem requires React for its components javascript to fully function. Follow the instructions in the [Ruby & React Setup](/guides/getting_started/rails_&_react_setup) guide to add react to your Rails app. + ## Unbundled Dependencies These kits require you to install additional libraries to get full functionality. diff --git a/playbook-website/app/views/guides/getting_started/react_setup.md b/playbook-website/app/views/guides/getting_started/react_setup.md index f6304d8d6a..1b0bf4dd4f 100644 --- a/playbook-website/app/views/guides/getting_started/react_setup.md +++ b/playbook-website/app/views/guides/getting_started/react_setup.md @@ -8,11 +8,13 @@ description: React applications. Endlessly flexible presentational UI components ```sh yarn add playbook-ui ``` -#### Match your project's versions of React and ReactDOM with Playbook's versions +#### Match your project's versions of React, ReactDOM, react-is and React Trix with Playbook's versions ```json "react": "17.0.2", "react-dom": "17.0.2", + "react-is": "^17.0.2", + "react-trix": "0.10.1", ``` #### Import fonts and CSS styles Can be imported in your Index.js file or top level app Component @@ -28,4 +30,7 @@ import 'playbook-ui/dist/fonts/fontawesome-min'; import { Avatar, Button } from 'playbook-ui'; ``` #### CodeSandbox React Setup Example -[Link to CodeSandbox Example](https://codesandbox.io/s/playbook-empty-6ixcw) \ No newline at end of file +[Link to CodeSandbox Example](https://codesandbox.io/s/playbook-empty-6ixcw) + +### Dependencies +[More details about Playbook dependencies](/guides/getting_started/dependencies) From 8fb787bdde56418f8c5438f3a831c2b45e8e3e6a Mon Sep 17 00:00:00 2001 From: Gary Kang <42440452+kangaree@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:19:36 -0500 Subject: [PATCH 11/14] [PLAY-1619] Font Awesome Setup Docs: JS Bundle and Asset Pipeline - Phase 2 (#3878) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **What does this PR do?** - Use `<details>` and `<summary>` to make collapsibles for different methods to install Font Awesome - Convert the existing Asset Pipeline docs to html to be used with `<details>` and `<summary>` - Add a new "With a JavaScript Bundler" section **Screenshots:** Screenshots to visualize your addition/change ![Screenshot 2024-11-04 at 9 04 48 AM](https://github.com/user-attachments/assets/32e99faf-2370-4214-9101-fb8a23333bc2) **How to test?** Steps to confirm the desired behavior: 1. Go to /guides/getting_started/font_awesome 2. There should be two "collapsibles": Default with an Asset Pipeline and With a JavaScript Bundler 3. When you click it, the sections should expand and contract with details 4. Review the new "With a JavaScript Bundler" section #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. --- .../guides/getting_started/font_awesome.md | 128 +++++++++++++----- 1 file changed, 91 insertions(+), 37 deletions(-) diff --git a/playbook-website/app/views/guides/getting_started/font_awesome.md b/playbook-website/app/views/guides/getting_started/font_awesome.md index 732a79757b..d207dda012 100644 --- a/playbook-website/app/views/guides/getting_started/font_awesome.md +++ b/playbook-website/app/views/guides/getting_started/font_awesome.md @@ -17,57 +17,111 @@ Integrating Font Awesome with Playbook ensures that you have access to these ben ![fontawesome](https://github.com/user-attachments/assets/638b63ad-56d3-4819-8e05-fcbb175bedc7) -## Ruby on Rails Setup (default with asset pipeline) - -**Make sure you are on Rails 7 or higher.** - -**1.** Follow the [Ruby on Rails Setup getting started page](/guides/getting_started/ruby_on_rails_setup) to setup Playbook with your Rails project. - -**2.** Setup Pro or Free Font Awesome to use our Icon Component. +### Ruby on Rails Setup + +<details class="mt_sm"> + <summary class="mb_sm"><strong><img src="https://github.com/user-attachments/assets/781b1ec8-954c-4919-a79c-7009521849a6" alt="rails logo" class="pb_custom_icon svg-inline--fa svg_fw mr_xxs" style="margin: 0;" />Default with an Asset Pipeline</strong></summary> + <strong>Make sure you are on Rails 7 or higher.</strong> + <p> + <strong>1.</strong> Follow the <a href="/guides/getting_started/ruby_on_rails_setup">Ruby on Rails Setup getting started page</a> to setup Playbook with your Rails project. + </p> + <p> + <strong>2.</strong> Setup Pro or Free Font Awesome to use our Icon Component. + </p> + <p><strong>Pro:</strong></p> + <pre><code class="rb"># app/assets/stylesheets/application.scss + @import "font-awesome-pro"; + @import "font-awesome-pro/solid"; + @import "font-awesome-pro/regular"; + @import "playbook";</code></pre> + <pre><code class="rb"># app/Gemfile + source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do + gem "font-awesome-pro-sass", "6.2.0" + end</code></pre> + <strong>Free:</strong> + <p><em>Currently only <a href="https://fontawesome.com/search?o=r&m=free&s=regular">Free Regular</a> icons are supported in our icon component structure.</em></p> -**Pro:** + <pre><code class="rb"># app/assets/stylesheets/application.scss + @import "font-awesome";</code></pre> -```rb -# app/assets/stylesheets/application.scss + <pre><code class="rb"># app/Gemfile + source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do + gem "font-awesome-pro-sass", "6.2.0" + end</code></pre> + + <strong>3.</strong> Bundle all the things! + + <pre><code class="sh">bundle install</code></pre> + + <strong>4.</strong> <strong>Go build awesome stuff!</strong> + + <p>Refer to our <a href="/kits/icon">Icon kit</a> to get started with Font Awesome icons in Playbook.</p> + + <pre><code class="rb"><%= pb_rails("icon", props: { icon: "font-awesome", fixed_width: true }) %></code></pre> +</details> + +<details class="mt_sm"> + <summary class="mb_sm"><strong><img src="https://github.com/user-attachments/assets/781b1ec8-954c-4919-a79c-7009521849a6" alt="rails logo" class="pb_custom_icon svg-inline--fa svg_fw mr_xxs" style="margin: 0;" />With a JavaScript Bundler</strong></summary> + <strong>Make sure you are on Rails 7 or higher.</strong> + <p> + <strong>1.</strong> Follow the <a href="/guides/getting_started/ruby_on_rails_setup">Ruby on Rails Setup getting started page</a> to setup Playbook with your Rails project. + </p> + <p> + Use your desired bundler: + <pre><code class="sh">rails new CoolNewApp -j webpack</code></pre> + </p> + <p> + <strong>2.</strong> Follow the <a href="/guides/getting_started/rails_&_react_setup">Ruby & React page</a> if you want to use React with your project. + </p> + <p> + <strong>3.</strong> Setup Pro or Free Font Awesome to use our Icon Component. + </p> + <p><strong>Pro:</strong></p> + <pre><code class="rb"># app/assets/stylesheets/application.scss @import "font-awesome-pro"; @import "font-awesome-pro/solid"; @import "font-awesome-pro/regular"; - @import "playbook"; -``` + @import "playbook";</code></pre> + <pre><code class="rb"># app/Gemfile + source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do + gem "font-awesome-pro-sass", "6.2.0" + end</code></pre> + <p>If you prefer, you can install with JavaScript:</p> + <pre><code class="sh">FONTAWESOME_PACKAGE_TOKEN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX yarn add @fortawesome/fontawesome-pro</code></pre> + <strong>Free:</strong> + <p><em>Currently only <a href="https://fontawesome.com/search?o=r&m=free&s=regular">Free Regular</a> icons are supported in our icon component structure.</em></p> + + <pre><code class="rb"># app/assets/stylesheets/application.scss + @import "font-awesome";</code></pre> -```rb -# app/Gemfile + <pre><code class="rb"># app/Gemfile source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do gem "font-awesome-pro-sass", "6.2.0" - end -``` + end</code></pre> -**Free:** + <p>If you prefer, you can install with JavaScript:</p> + <pre><code class="sh">yarn add @fortawesome/fontawesome-free</code></pre> -*Currently only [Free Regular](https://fontawesome.com/search?o=r&m=free&s=regular) icons are supported in our icon component structure.* + <strong>4.</strong> Bundle all the things! -```rb -# app/assets/stylesheets/application.scss - @import "font-awesome"; -``` + <pre><code class="sh">bundle install</code></pre> -```rb -# app/Gemfile - source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do - gem "font-awesome-pro-sass", "6.2.0" - end -``` + <pre><code class="sh">yarn</code></pre> + + <pre><code class="sh">npm install</code></pre> + + <strong>5.</strong> Build JavaScript for development + <p>When using a bundling option, use <code>bin/dev</code> to start the Rails server and build JavaScript for development. Don't forget to add a build script in your package.json file:</p> -**3.** Bundle all the things! + <pre><code class="js">"scripts": { + "build": "webpack" + },</code></pre> -```sh -bundle install -``` + <strong>6.</strong> <strong>Go build awesome stuff!</strong> -**4.** **Go build awesome stuff!** + <p>Refer to our <a href="/kits/icon">Icon kit</a> to get started with Font Awesome icons in Playbook.</p> -Refer to our [Icon kit](/kits/icon) to get started with Font Awesome icons in Playbook. + <pre><code class="rb"><%= pb_rails("icon", props: { icon: "font-awesome", fixed_width: true }) %></code></pre> -```rb -<%= pb_rails("icon", props: { icon: "font-awesome", fixed_width: true }) %> -``` + <pre><code class="react"><Icon fixedWidth icon="font-awesome" /></code></pre> +</details> \ No newline at end of file From b0e3077a717fef72dd891df7f5595a2e4285b51b Mon Sep 17 00:00:00 2001 From: Sam Duncan <samkduncan@gmail.com> Date: Wed, 6 Nov 2024 11:08:01 -0600 Subject: [PATCH 12/14] [PLAY-1550] Bump lazysizes from 5.2.2 to 5.3.2 (#3855) **What does this PR do?** A clear and concise description with your runway ticket url. https://runway.powerhrg.com/backlog_items/PLAY-1550 Dependency upgrade for 'lazysizes' #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. --- playbook/package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/playbook/package.json b/playbook/package.json index 2af3422d19..f6f6a39243 100644 --- a/playbook/package.json +++ b/playbook/package.json @@ -75,7 +75,7 @@ "jest": "26.6.3", "jest-axe": "4.1.0", "jest-fail-on-console": "3.2.0", - "lazysizes": "^5.2.2", + "lazysizes": "^5.3.2", "lodash-es": "^4.17.12", "react": "^17.0.2", "react-animate-height": "^2.0.23", diff --git a/yarn.lock b/yarn.lock index bc890a2c0b..1fbf2aa399 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8433,7 +8433,7 @@ kleur@^3.0.3: resolved "https://npm.powerapp.cloud/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -lazysizes@^5.2.2: +lazysizes@^5.3.2: version "5.3.2" resolved "https://npm.powerapp.cloud/lazysizes/-/lazysizes-5.3.2.tgz#27f974c26f5fcc33e7db765c0f4930488c8a2984" integrity sha512-22UzWP+Vedi/sMeOr8O7FWimRVtiNJV2HCa+V8+peZOw6QbswN9k58VUhd7i6iK5bw5QkYrF01LJbeJe0PV8jg== From c69799dc40420f47945ef05b2e1b734c00720a41 Mon Sep 17 00:00:00 2001 From: Carlos Lima <carloslimasd@gmail.com> Date: Thu, 7 Nov 2024 14:15:29 -0300 Subject: [PATCH 13/14] [PBNTR-633] Fixing parsing kits props (#3857) **What does this PR do?** Fixing parsing kits props **How to test?** Steps to confirm the desired behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See addition/change #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. - [ ] **TESTS** I have added test coverage to my code. --- .../pb_kits/playbook/pb_dropdown/_dropdown.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx index 13af9cb3c5..b946f7f295 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +++ b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx @@ -47,7 +47,7 @@ interface DropdownComponent Container: typeof DropdownContainer; } -const Dropdown = forwardRef((props: DropdownProps, ref: any) => { +let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => { const { aria = {}, autocomplete = false, @@ -260,7 +260,7 @@ const Dropdown = forwardRef((props: DropdownProps, ref: any) => { <DropdownContainer> {optionsWithBlankSelection && optionsWithBlankSelection?.map((option: GenericObject) => ( - <Dropdown.Option key={option.id} + <DropdownOption key={option.id} option={option} /> ))} @@ -278,11 +278,12 @@ const Dropdown = forwardRef((props: DropdownProps, ref: any) => { </DropdownContext.Provider> </div> ) -}) as DropdownComponent +} -Dropdown.displayName = "Dropdown"; -Dropdown.Option = DropdownOption; -Dropdown.Trigger = DropdownTrigger; -Dropdown.Container = DropdownContainer; +Dropdown = forwardRef(Dropdown) as unknown as DropdownComponent; +(Dropdown as DropdownComponent).displayName = "Dropdown"; +(Dropdown as DropdownComponent).Option = DropdownOption; +(Dropdown as DropdownComponent).Trigger = DropdownTrigger; +(Dropdown as DropdownComponent).Container = DropdownContainer; export default Dropdown; From 95eef34b1158cc9522ffaeeac7be53121ad8a3f3 Mon Sep 17 00:00:00 2001 From: Mark Rosenberg <38965626+markdoeswork@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:25:52 -0500 Subject: [PATCH 14/14] [PLAY-1587] Group hover Global Prop (#3833) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **What does this PR do?** A clear and concise description with your runway ticket url. Runway https://runway.powerhrg.com/backlog_items/PLAY-1587 This story adds a new global prop, Group Hover When you add the `group_hover` prop to a kit and the children inside that kit. The children's hover effects will take place when the parent is hovered. You just need to pass the `group_hover: true` or `groupHover` global prop to the parent and children(s) **Screenshots:** Screenshots to visualize your addition/change ![Screenshot 2024-10-31 at 11 11 30 AM](https://github.com/user-attachments/assets/7f119416-8b72-4e5c-b9e9-a7e5732273eb) **How to test?** Steps to confirm the desired behavior: 1. Go to https://pr3833.playbook.beta.gm.powerapp.cloud/visual_guidelines/group_hover 2. hover over the card/text in the card to see the different behaviors #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. - [😈] **TESTS** I have added test coverage to my code. --- .../MenuData/GuidelinesNavItems.ts | 4 + .../VisualGuidelines/Examples/GroupHover.tsx | 49 ++++++++++ .../components/VisualGuidelines/index.tsx | 3 + .../pages/code_snippets/group_hover_jsx.txt | 21 +++++ .../pb_kits/playbook/utilities/_hover.scss | 89 ++++++++++--------- .../playbook/utilities/globalPropNames.mjs | 1 + .../pb_kits/playbook/utilities/globalProps.ts | 7 +- playbook/lib/playbook/hover.rb | 5 +- 8 files changed, 134 insertions(+), 45 deletions(-) create mode 100644 playbook-website/app/javascript/components/VisualGuidelines/Examples/GroupHover.tsx create mode 100644 playbook-website/app/views/pages/code_snippets/group_hover_jsx.txt diff --git a/playbook-website/app/javascript/components/MainSidebar/MenuData/GuidelinesNavItems.ts b/playbook-website/app/javascript/components/MainSidebar/MenuData/GuidelinesNavItems.ts index 9303a1d3f7..da7e613fbd 100644 --- a/playbook-website/app/javascript/components/MainSidebar/MenuData/GuidelinesNavItems.ts +++ b/playbook-website/app/javascript/components/MainSidebar/MenuData/GuidelinesNavItems.ts @@ -63,6 +63,10 @@ export const VisualGuidelinesItems = [ name: "Hover", link: "/visual_guidelines/hover" }, + { + name: "Group Hover", + link: "/visual_guidelines/group_hover" + }, { name: "Text Align", link: "/visual_guidelines/text_align" diff --git a/playbook-website/app/javascript/components/VisualGuidelines/Examples/GroupHover.tsx b/playbook-website/app/javascript/components/VisualGuidelines/Examples/GroupHover.tsx new file mode 100644 index 0000000000..4e262da48b --- /dev/null +++ b/playbook-website/app/javascript/components/VisualGuidelines/Examples/GroupHover.tsx @@ -0,0 +1,49 @@ +/* eslint-disable flowtype/no-types-missing-file-annotation */ + +import React from 'react' + +import { + Card, + Title, +} from 'playbook-ui' + +import Example from '../Templates/Example' + +const GroupHoverDescription = () => ( + <> + You can hover over a kit and its children's hover affects will be applied. Check out <a href="https://playbook.powerapp.cloud/visual_guidelines/hover">{"our hover affects here."}</a> + </> +) + +const GroupHover = ({ example }: {example: string}) => ( + <Example + backgroundClass='group-hover-class' + description={<GroupHoverDescription />} + example={example} + title="Group Hover" + > + <Card + cursor="pointer" + groupHover + > + <Title + alignSelf="center" + groupHover + hover={{ scale: "lg"}} + text="If the card is hovered, I'm hovered too!" + /> + <Title + alignSelf="center" + paddingY="lg" + text="I don't have any hover effect on me" + /> + <Title + alignSelf="center" + hover={{ scale: "lg"}} + text="I need to be hovered over directly" + /> + </Card> + </Example> +) + +export default GroupHover diff --git a/playbook-website/app/javascript/components/VisualGuidelines/index.tsx b/playbook-website/app/javascript/components/VisualGuidelines/index.tsx index 98356e6aa8..043d89d30a 100644 --- a/playbook-website/app/javascript/components/VisualGuidelines/index.tsx +++ b/playbook-website/app/javascript/components/VisualGuidelines/index.tsx @@ -18,6 +18,7 @@ import Cursor from "../VisualGuidelines/Examples/Cursor"; import FlexBox from "../VisualGuidelines/Examples/FlexBox"; import Position from "../VisualGuidelines/Examples/Position"; import Hover from "../VisualGuidelines/Examples/Hover"; +import GroupHover from "../VisualGuidelines/Examples/GroupHover"; import TextAlign from "../VisualGuidelines/Examples/TextAlign"; import Overflow from "./Examples/Overflow"; import Truncate from "./Examples/Truncate"; @@ -82,6 +83,8 @@ const VisualGuidelines = ({ return <VerticalAlign example={examples.vertical_align_jsx}/>; case "hover": return <Hover example={examples.hover_jsx}/>; + case "group_hover": + return <GroupHover example={examples.group_hover_jsx}/>; case "text_align": return <TextAlign example={examples.text_align_jsx} /> case "overflow": diff --git a/playbook-website/app/views/pages/code_snippets/group_hover_jsx.txt b/playbook-website/app/views/pages/code_snippets/group_hover_jsx.txt new file mode 100644 index 0000000000..6366242b5d --- /dev/null +++ b/playbook-website/app/views/pages/code_snippets/group_hover_jsx.txt @@ -0,0 +1,21 @@ +<Card + cursor="pointer" + groupHover +> + <Title + alignSelf="center" + groupHover + hover={{ scale: "lg"}} + text="If the card is hovered, I'm hovered too!" + /> + <Title + alignSelf="center" + paddingY="lg" + text="I don't have any hover effect on me" + /> + <Title + alignSelf="center" + hover={{ scale: "lg"}} + text="I need to be hovered over directly" + /> +</Card> diff --git a/playbook/app/pb_kits/playbook/utilities/_hover.scss b/playbook/app/pb_kits/playbook/utilities/_hover.scss index 853d835123..c5c6918709 100644 --- a/playbook/app/pb_kits/playbook/utilities/_hover.scss +++ b/playbook/app/pb_kits/playbook/utilities/_hover.scss @@ -1,51 +1,54 @@ @import "../tokens/exports/scale.module"; -@mixin hover-color-classes($colors-list) { - @each $name, $color in $colors-list { - .hover_background_#{"" + $name}:hover { - background-color: $color !important; - transition: background-color $transition-speed ease; - } - .hover_color_#{"" + $name}:hover { - color: $color !important; - transition: color $transition-speed ease; - } +@mixin hover-scale-classes($scales-list) { + @each $name, $scale in $scales-list { + .hover_#{"" + $name}:hover, + .group_hover:hover .group_hover.hover_#{"" + $name} { + transform: $scale; + transition: transform $transition-speed ease; } } - - @mixin hover-shadow-classes($shadows-list) { - @each $name, $shadow in $shadows-list { - .hover_#{"" + $name}:hover { - box-shadow: $shadow; - transition: box-shadow $transition-speed ease; - } +} + +@mixin hover-shadow-classes($shadows-list) { + @each $name, $shadow in $shadows-list { + .hover_#{"" + $name}:hover, + .group_hover:hover .group_hover.hover_#{"" + $name} { + box-shadow: $shadow; + transition: box-shadow $transition-speed ease; } } - - @mixin hover-scale-classes($scales-list) { - @each $name, $scale in $scales-list { - .hover_#{"" + $name}:hover { - transform: $scale; - transition: transform $transition-speed ease; - } +} + +@mixin hover-color-classes($colors-list) { + @each $name, $color in $colors-list { + .hover_background_#{"" + $name}:hover, + .group_hover:hover .group_hover.hover_background_#{"" + $name} { + background-color: $color !important; + transition: background-color $transition-speed ease; + } + .hover_color_#{"" + $name}:hover, + .group_hover:hover .group_hover.hover_color_#{"" + $name} { + color: $color !important; + transition: color $transition-speed ease; } } - - - @include hover-scale-classes($scales); - @include hover-shadow-classes($box_shadows); - @include hover-color-classes($product_colors); - @include hover-color-classes($status_colors); - @include hover-color-classes($data_colors); - @include hover-color-classes($shadow_colors); - @include hover-color-classes($colors); - @include hover-color-classes($interface_colors); - @include hover-color-classes($main_colors); - @include hover-color-classes($background_colors); - @include hover-color-classes($card_colors); - @include hover-color-classes($active_colors); - @include hover-color-classes($action_colors); - @include hover-color-classes($hover_colors); - @include hover-color-classes($border_colors); - @include hover-color-classes($text_colors); - @include hover-color-classes($category_colors); \ No newline at end of file +} + +@include hover-scale-classes($scales); +@include hover-shadow-classes($box_shadows); +@include hover-color-classes($product_colors); +@include hover-color-classes($status_colors); +@include hover-color-classes($data_colors); +@include hover-color-classes($shadow_colors); +@include hover-color-classes($colors); +@include hover-color-classes($interface_colors); +@include hover-color-classes($main_colors); +@include hover-color-classes($background_colors); +@include hover-color-classes($card_colors); +@include hover-color-classes($active_colors); +@include hover-color-classes($action_colors); +@include hover-color-classes($hover_colors); +@include hover-color-classes($border_colors); +@include hover-color-classes($text_colors); +@include hover-color-classes($category_colors); diff --git a/playbook/app/pb_kits/playbook/utilities/globalPropNames.mjs b/playbook/app/pb_kits/playbook/utilities/globalPropNames.mjs index d41b00be0b..217f976a8d 100644 --- a/playbook/app/pb_kits/playbook/utilities/globalPropNames.mjs +++ b/playbook/app/pb_kits/playbook/utilities/globalPropNames.mjs @@ -4,6 +4,7 @@ export default [ "right", "top", "hover", + "groupHover", "zIndex", "verticalAlign", "truncate", diff --git a/playbook/app/pb_kits/playbook/utilities/globalProps.ts b/playbook/app/pb_kits/playbook/utilities/globalProps.ts index 9dd67b7223..3f6e2f2f88 100644 --- a/playbook/app/pb_kits/playbook/utilities/globalProps.ts +++ b/playbook/app/pb_kits/playbook/utilities/globalProps.ts @@ -66,6 +66,10 @@ type Hover = Shadow & { scale?: "sm" | "md" | "lg" } +type GroupHover = { + groupHover?: boolean, +} + type JustifyContent = { justifyContent?: Alignment & Space } @@ -175,7 +179,7 @@ export type GlobalProps = AlignContent & AlignItems & AlignSelf & BorderRadius & Cursor & Dark & Display & DisplaySizes & Flex & FlexDirection & FlexGrow & FlexShrink & FlexWrap & JustifyContent & JustifySelf & LineHeight & Margin & MinWidth & MaxWidth & NumberSpacing & Order & Overflow & Padding & - Position & Shadow & TextAlign & Truncate & VerticalAlign & ZIndex & { hover?: string } & Top & Right & Bottom & Left; + Position & Shadow & TextAlign & Truncate & VerticalAlign & ZIndex & GroupHover & { hover?: string } & Top & Right & Bottom & Left; const getResponsivePropClasses = (prop: {[key: string]: string}, classPrefix: string) => { const keys: string[] = Object.keys(prop) @@ -209,6 +213,7 @@ const filterClassName = (value: string): string => { // Prop categories const PROP_CATEGORIES: {[key:string]: (props: {[key: string]: any}) => string} = { + groupHoverProps: ({ groupHover }: GroupHover ) => groupHover ? 'group_hover ' : '', hoverProps: ({ hover }: { hover?: Hover }) => { let css = ''; if (!hover) return css; diff --git a/playbook/lib/playbook/hover.rb b/playbook/lib/playbook/hover.rb index 769947b477..e3cf074d00 100644 --- a/playbook/lib/playbook/hover.rb +++ b/playbook/lib/playbook/hover.rb @@ -4,6 +4,7 @@ module Playbook module Hover def self.included(base) base.prop :hover + base.prop :group_hover, type: Playbook::Props::Boolean, default: false end def hover_options @@ -38,7 +39,8 @@ def hover_attributes def hover_props selected_props = hover_options.keys.select { |sk| try(sk) } - return nil unless selected_props.present? + + return nil if selected_props.nil? && group_hover.nil? responsive = selected_props.present? && try(selected_props.first).is_a?(::Hash) css = "" @@ -58,6 +60,7 @@ def hover_props end end + css += "group_hover " if group_hover css.strip unless css.blank? end end