diff --git a/.github/workflows/github-actions-check-labels.yml b/.github/workflows/github-actions-check-labels.yml
index e69de29bb2..e369390c95 100644
--- a/.github/workflows/github-actions-check-labels.yml
+++ b/.github/workflows/github-actions-check-labels.yml
@@ -0,0 +1,35 @@
+name: Check Labels
+run-name: ${{ github.actor }} is checking labels π
+on:
+ pull_request:
+ types: [ labeled, unlabeled ]
+jobs:
+ Checking-Labels:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check for Semver label
+ run: |
+ LABELS=$(jq -r '.pull_request.labels[].name' "$GITHUB_EVENT_PATH")
+ SEMVER_PATTERN="^(major|minor|patch)$"
+
+ SEMVER_LABELS=$(echo "$LABELS" | grep -iE "$SEMVER_PATTERN" || true)
+
+ # Check if SEMVER_LABELS is empty
+ if [ -z "$SEMVER_LABELS" ]; then
+ echo "Error: No Semver label found. Please add exactly one of: major, minor, patch (case-insensitive)"
+ exit 1
+ fi
+
+ SEMVER_LABEL_COUNT=$(echo "$SEMVER_LABELS" | wc -l)
+
+ if [ "$SEMVER_LABEL_COUNT" -eq 0 ]; then
+ echo "Error: No Semver label found. Please add exactly one of: major, minor, patch (case-insensitive)"
+ exit 1
+ elif [ "$SEMVER_LABEL_COUNT" -gt 1 ]; then
+ echo "Error: Multiple Semver labels found. Please ensure only one is present:"
+ echo "$SEMVER_LABELS"
+ exit 1
+ else
+ NORMALIZED_LABEL=$(echo "$SEMVER_LABELS" | tr '[:upper:]' '[:lower:]')
+ echo "Valid Semver label found: $NORMALIZED_LABEL"
+ fi
\ No newline at end of file
diff --git a/.github/workflows/github-actions-release-candidate.yml b/.github/workflows/github-actions-release-candidate.yml
index 0df9bed6c3..633f7698f0 100644
--- a/.github/workflows/github-actions-release-candidate.yml
+++ b/.github/workflows/github-actions-release-candidate.yml
@@ -30,29 +30,88 @@ jobs:
with:
bundler-cache: true
- name: Python Setup
- uses: actions/setup-python@v4
- with:
+ uses: actions/setup-python@v4
+ with:
python-version: '3.9'
- name: Set Git Config
run: |
git config --local user.name "${{ github.actor }}"
git config --local user.email "${{ github.actor }}@users.noreply.github.com"
- - name: Grab Current Version and Set New RC Version
- id: set-version
+ - name: Get Semver Label
+ id: get-label
run: |
- current_npm_version=$(node -pe "require('./package.json').version")
+ PR_NUMBER=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
+ "https://api.github.com/repos/${{ github.repository }}/commits/${{ github.sha }}/pulls" \
+ | jq '.[0].number')
+ echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
- if [[ $current_npm_version == *"-rc."* ]]; then
- new_npm_version=$(yarn version --prerelease --preid rc --no-git-tag-version | grep "New version:" | awk '{print $4}')
+ if [ ! -z "$PR_NUMBER" ] && [ "$PR_NUMBER" != "null" ]; then
+ echo "β
Successfully found PR number: $PR_NUMBER"
else
- new_npm_version=$(yarn version --preminor --preid rc --no-git-tag-version | grep "New version:" | awk '{print $4}')
+ echo "β Unable to find PR number"
fi
-
+
+ echo "Fetching labels for PR #$PR_NUMBER..."
+ LABELS=$(gh pr view $PR_NUMBER --json labels -q '.labels[].name' || echo "Failed to fetch labels")
+ echo "Found labels: $LABELS"
+
+ if [ -z "$LABELS" ]; then
+ echo "β Error: Failed to fetch PR labels"
+ exit 1
+ fi
+
+ SEMVER_LABEL=$(echo "$LABELS" | grep -iE '^(major|minor|patch)$' || true)
+ echo "Found Semver labels: $SEMVER_LABEL"
+
+ if [ -z "$SEMVER_LABEL" ]; then
+ echo "β Error: No valid Semver label (major, minor, patch) found on PR #$PR_NUMBER."
+ exit 1
+ fi
+
+ LABEL_COUNT=$(echo "$SEMVER_LABEL" | wc -l)
+ echo "Number of Semver labels found: $LABEL_COUNT"
+
+ if [ "$LABEL_COUNT" -ne 1 ]; then
+ echo "β Error: Expected exactly one Semver label, found $LABEL_COUNT on PR #$PR_NUMBER."
+ exit 1
+ fi
+
+ echo "SEMVER_LABEL=$SEMVER_LABEL" >> $GITHUB_ENV
+ echo "β
Successfully found Semver label: $SEMVER_LABEL"
+
+ echo "SEMVER_LABEL=$SEMVER_LABEL" >> $GITHUB_ENV
+ echo "Semver label found: $SEMVER_LABEL"
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Grab Current Version and Set New RC Version
+ id: set-version
+ run: |
+ current_npm_version=$(node -pe "require('./package.json').version")
+
+ case ${{ env.SEMVER_LABEL }} in
+ Major)
+ new_npm_version=$(yarn version --premajor --preid rc --no-git-tag-version | grep "New version:" | awk '{print $4}')
+ ;;
+ Minor)
+ new_npm_version=$(yarn version --preminor --preid rc --no-git-tag-version | grep "New version:" | awk '{print $4}')
+ ;;
+ Patch)
+ new_npm_version=$(yarn version --prepatch --preid rc --no-git-tag-version | grep "New version:" | awk '{print $4}')
+ ;;
+ *)
+ echo "Error: Invalid Semver label: ${{ env.SEMVER_LABEL }}"
+ exit 1
+ ;;
+ esac
+
new_npm_version=${new_npm_version#v}
new_ruby_version=$(echo $new_npm_version | sed 's/-rc\./.pre.rc./')
-
+
echo "new_npm_version=${new_npm_version}" >> $GITHUB_ENV
echo "new_ruby_version=${new_ruby_version}" >> $GITHUB_ENV
+
- name: Check if version exists and increment if necessary
run: |
max_attempts=100
@@ -140,10 +199,10 @@ jobs:
issue_number: pullRequestNumber,
owner: context.repo.owner,
repo: context.repo.repo,
- body: `You merged this pr to master branch:
+ body: `You merged this pr to master branch:
- Ruby Gem: [${{env.RUBY_GEM_VERSION}}](${{env.RUBY_GEM_LINK}})
- NPM: [${{env.NPM_VERSION}}](${{env.NPM_LINK}})`
});
} else {
console.log('No pull request found for this commit');
- }
+ }
\ No newline at end of file
diff --git a/playbook-website/Gemfile.lock b/playbook-website/Gemfile.lock
index 884c2fb3af..2909c99b95 100644
--- a/playbook-website/Gemfile.lock
+++ b/playbook-website/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: ../playbook
specs:
- playbook_ui (14.6.2)
+ playbook_ui (14.7.0)
actionpack (>= 5.2.4.5)
actionview (>= 5.2.4.5)
activesupport (>= 5.2.4.5)
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 {"our hover affects here."}
+ >
+)
+
+const GroupHover = ({ example }: {example: string}) => (
+
+ 1. Follow the Ruby on Rails Setup getting started page to setup Playbook with your Rails project.
+
+ 2. Setup Pro or Free Font Awesome to use our Icon Component.
+ Pro: Currently only Free Regular icons are supported in our icon component structure. Refer to our Icon kit to get started with Font Awesome icons in Playbook.
+ 1. Follow the Ruby on Rails Setup getting started page to setup Playbook with your Rails project.
+
+ Use your desired bundler:
+
(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/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..d207dda012
--- /dev/null
+++ b/playbook-website/app/views/guides/getting_started/font_awesome.md
@@ -0,0 +1,127 @@
+---
+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 an Asset Pipeline
+ Make sure you are on Rails 7 or higher.
+
+ # app/assets/stylesheets/application.scss
+ @import "font-awesome-pro";
+ @import "font-awesome-pro/solid";
+ @import "font-awesome-pro/regular";
+ @import "playbook";
+ Free:
+ # app/Gemfile
+ source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do
+ gem "font-awesome-pro-sass", "6.2.0"
+ end
+
+ # app/assets/stylesheets/application.scss
+ @import "font-awesome";
+
+ 3. Bundle all the things!
+
+ # app/Gemfile
+ source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do
+ gem "font-awesome-pro-sass", "6.2.0"
+ end
+
+ 4. Go build awesome stuff!
+
+ bundle install
+<%= pb_rails("icon", props: { icon: "font-awesome", fixed_width: true }) %>
With a JavaScript Bundler
+ Make sure you are on Rails 7 or higher.
+
+ rails new CoolNewApp -j webpack
+ 2. Follow the Ruby & React page if you want to use React with your project. +
++ 3. Setup Pro or Free Font Awesome to use our Icon Component. +
+Pro:
+# app/assets/stylesheets/application.scss
+ @import "font-awesome-pro";
+ @import "font-awesome-pro/solid";
+ @import "font-awesome-pro/regular";
+ @import "playbook";
+ # app/Gemfile
+ source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do
+ gem "font-awesome-pro-sass", "6.2.0"
+ end
+ If you prefer, you can install with JavaScript:
+FONTAWESOME_PACKAGE_TOKEN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX yarn add @fortawesome/fontawesome-pro
+ Free:
+ Currently only Free Regular icons are supported in our icon component structure.
+ +# app/assets/stylesheets/application.scss
+ @import "font-awesome";
+
+ # app/Gemfile
+ source "https://token:TOKEN@dl.fontawesome.com/basic/fontawesome-pro/ruby/" do
+ gem "font-awesome-pro-sass", "6.2.0"
+ end
+
+ If you prefer, you can install with JavaScript:
+yarn add @fortawesome/fontawesome-free
+
+ 4. Bundle all the things!
+
+ bundle install
+
+ yarn
+
+ npm install
+
+ 5. Build JavaScript for development
+ When using a bundling option, use bin/dev
to start the Rails server and build JavaScript for development. Don't forget to add a build script in your package.json file:
"scripts": {
+ "build": "webpack"
+ },
+
+ 6. Go build awesome stuff!
+
+ Refer to our Icon kit to get started with Font Awesome icons in Playbook.
+ +<%= pb_rails("icon", props: { icon: "font-awesome", fixed_width: true }) %>
+
+ <Icon fixedWidth icon="font-awesome" />
+
\ No newline at end of file
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)
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 @@
+( + element: React.ReactNode, + component: React.ComponentType
+): element is React.ReactElement
{ + return React.isValidElement
(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 ( -