From 1ec84ae4f40f8b287865ee7618a5e75cfc76d89c Mon Sep 17 00:00:00 2001 From: Nida Ghuman Date: Fri, 4 Oct 2024 12:43:02 -0400 Subject: [PATCH 01/31] Update CHANGELOG.md 14.5.0 (#3750) **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. --- playbook/CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/playbook/CHANGELOG.md b/playbook/CHANGELOG.md index 75c3509657..7080805df6 100644 --- a/playbook/CHANGELOG.md +++ b/playbook/CHANGELOG.md @@ -1,3 +1,40 @@ +# 🐝 Improved Usability, Flexibility, Customization and More! 🐝 +##### October 1, 2024 + +![1450](https://github.com/user-attachments/assets/04a54556-9148-4a9e-8ff2-4902c59aef18) + +We’re back! 14.5.0 is live in Nitro and is packed with improvements, bug fixes and more! +This release focuses on continued improvements to usability, customization, and visual consistency! + +[14.5.0](https://github.com/powerhome/playbook/tree/14.5.0) full list of changes: + +**Kit Enhancements:** +- Enable Kits Instead of Text for Radio Label - React Only [\#3691](https://github.com/powerhome/playbook/pull/3691) ([nickamantia](https://github.com/nickamantia)) +- Remove marginBottom from Typeahead Kit [\#3680](https://github.com/powerhome/playbook/pull/3680) ([elisashapiro](https://github.com/elisashapiro)) + + +**Fixed Bugs:** +- Fixing Minimizing the Dropdown When Clicking Outside [\#3688](https://github.com/powerhome/playbook/pull/3688) ([carloslimasd](https://github.com/carloslimasd)) +- Make Dialog Close 'x' Icons Same Size [\#3706](https://github.com/powerhome/playbook/pull/3706) ([kangaree](https://github.com/kangaree)) + + + +**Improvements:** +- Bump Highcharts Packages to Latest [\#3708](https://github.com/powerhome/playbook/pull/3708) ([kangaree](https://github.com/kangaree)) +- Remove FA from the Playbook Website [\#3687](https://github.com/powerhome/playbook/pull/3687) ([skduncan](https://github.com/skduncan)) +- Add Comment When Merge to Master for RC [\#3727](https://github.com/powerhome/playbook/pull/3727) ([markdoeswork](https://github.com/markdoeswork)) +- Fix Spacing on Radio kit Children Doc Example [\#3730](https://github.com/powerhome/playbook/pull/3730) ([elisashapiro](https://github.com/elisashapiro)) +- Add Multiple Pagination Components Example and Dynamic Key Copy [\#3715](https://github.com/powerhome/playbook/pull/3715) ([elisashapiro](https://github.com/elisashapiro)) +- Bump to ci-kubed v8.6.0 [no-cache] [\#3639](https://github.com/powerhome/playbook/pull/3639) ([TeamTeaTime](https://github.com/TeamTeaTime)) +- Add Comment Script for RCs [\#3717](https://github.com/powerhome/playbook/pull/3717) ([markdoeswork](https://github.com/markdoeswork)) +- Use New Icons [\#3648](https://github.com/powerhome/playbook/pull/3648) ([markdoeswork](https://github.com/markdoeswork)) + +**New Kits:** +- Drawer Beta Kit [\#3686](https://github.com/powerhome/playbook/pull/3686) ([markdoeswork](https://github.com/markdoeswork)) + + +[Full Changelog](https://github.com/powerhome/playbook/compare/14.4.0...14.5.0) + # Spot Everyone at a Glance: Meet the New Multiple Users Component! ##### September 18, 2024 From caa3c8ad471a78490a0c39f92220551889ceebe0 Mon Sep 17 00:00:00 2001 From: Nida Ghuman Date: Fri, 4 Oct 2024 13:24:17 -0400 Subject: [PATCH 02/31] [PBNTR-529] Children for MultiLevelSelect (#3661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Runway Story](https://runway.powerhrg.com/backlog_items/PBNTR-529) This PR: - ✅ Creates MultiLevelSelect.Options subcomponent - ✅ Backwards compatible so none subcomponent structure also works - ✅ Render children next to Checkbox/Radios Screenshot 2024-10-01 at 9 41 45 PM Screenshot 2024-10-01 at 9 42 02 PM --- .../_multi_level_select.tsx | 423 ++++++++---------- .../pb_multi_level_select/context/index.tsx | 5 + .../docs/_multi_level_select_default.jsx | 2 +- .../_multi_level_select_with_children.jsx | 105 +++++ .../docs/_multi_level_select_with_children.md | 1 + ...level_select_with_children_with_radios.jsx | 106 +++++ ..._level_select_with_children_with_radios.md | 1 + .../pb_multi_level_select/docs/example.yml | 3 + .../pb_multi_level_select/docs/index.js | 2 + .../multi_level_select_options.tsx | 149 ++++++ 10 files changed, 568 insertions(+), 229 deletions(-) create mode 100644 playbook/app/pb_kits/playbook/pb_multi_level_select/context/index.tsx create mode 100644 playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx create mode 100644 playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.md create mode 100644 playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx create mode 100644 playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.md create mode 100644 playbook/app/pb_kits/playbook/pb_multi_level_select/multi_level_select_options.tsx diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx b/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx index 911d22ab02..07cd852766 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx @@ -1,14 +1,17 @@ -import React, { useState, useEffect, useRef } from "react" -import classnames from "classnames" -import { globalProps, GlobalProps } from "../utilities/globalProps" -import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props" -import Checkbox from "../pb_checkbox/_checkbox" -import Radio from "../pb_radio/_radio" -import Body from "../pb_body/_body" -import Icon from "../pb_icon/_icon" -import FormPill from "../pb_form_pill/_form_pill" -import CircleIconButton from "../pb_circle_icon_button/_circle_icon_button" -import { cloneDeep } from "lodash" +import React, { useState, useEffect, useRef } from "react"; +import classnames from "classnames"; +import { globalProps, GlobalProps } from "../utilities/globalProps"; +import { + buildAriaProps, + buildCss, + buildDataProps, + buildHtmlProps, +} from "../utilities/props"; +import Icon from "../pb_icon/_icon"; +import FormPill from "../pb_form_pill/_form_pill"; +import { cloneDeep } from "lodash"; +import MultiLevelSelectOptions from "./multi_level_select_options"; +import MultiLevelSelectContext from "./context"; import { getAncestorsOfUnchecked, @@ -18,7 +21,7 @@ import { getDefaultCheckedItems, recursiveCheckParent, getExpandedItems, -} from "./_helper_functions" +} from "./_helper_functions"; type MultiLevelSelectProps = { aria?: { [key: string]: string } @@ -30,9 +33,9 @@ type MultiLevelSelectProps = { inputName?: string name?: string returnAllSelected?: boolean - treeData?: { [key: string]: string }[] + treeData?: { [key: string]: string; }[] | any onSelect?: (prop: { [key: string]: any }) => void - selectedIds?: string[] + selectedIds?: string[] | any variant?: "multi" | "single" pillColor?: "primary" | "neutral" | "success" | "warning" | "error" | "info" | "data_1" | "data_2" | "data_3" | "data_4" | "data_5" | "data_6" | "data_7" | "data_8" | "windows" | "siding" | "roofing" | "doors" | "gutters" | "solar" | "insulation" | "accessories", } & GlobalProps @@ -52,126 +55,132 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => { onSelect = () => null, selectedIds, variant = "multi", + children, pillColor = "primary" } = props - const ariaProps = buildAriaProps(aria) - const dataProps = buildDataProps(data) - const htmlProps = buildHtmlProps(htmlOptions) + const ariaProps = buildAriaProps(aria); + const dataProps = buildDataProps(data); + const htmlProps = buildHtmlProps(htmlOptions); const classes = classnames( buildCss("pb_multi_level_select"), globalProps(props), className - ) + ); - const dropdownRef = useRef(null) + const dropdownRef = useRef(null); // State for whether dropdown is open or closed - const [isDropdownClosed, setIsDropdownClosed] = useState(true) + const [isDropdownClosed, setIsDropdownClosed] = useState(true); // State from onChange for textinput, to use for filtering to create typeahead - const [filterItem, setFilterItem] = useState("") + const [filterItem, setFilterItem] = useState(""); // FormattedData with checked and parent_id added - const [formattedData, setFormattedData] = useState([]) + const [formattedData, setFormattedData] = useState([]); // State for the return of returnAllSelected - const [returnedArray, setReturnedArray] = useState([]) + const [returnedArray, setReturnedArray] = useState([]); // State for default return - const [defaultReturn, setDefaultReturn] = useState([]) + const [defaultReturn, setDefaultReturn] = useState([]); // Get expanded items from treeData - const initialExpandedItems = getExpandedItems(treeData, selectedIds) + const initialExpandedItems = getExpandedItems(treeData, selectedIds); // Initialize state with expanded items - const [expanded, setExpanded] = useState(initialExpandedItems) + const [expanded, setExpanded] = useState(initialExpandedItems); // Single Select specific state const [singleSelectedItem, setSingleSelectedItem] = useState({ id: [], value: "", - item: [] - }) + item: [], + }); const arrowDownElementId = `arrow_down_${id}` const arrowUpElementId = `arrow_up_${id}` const modifyRecursive = (tree: { [key: string]: any }[], check: boolean) => { if (!Array.isArray(tree)) { - return + return; } return tree.map((item: { [key: string]: any }) => { - item.checked = check - item.children = modifyRecursive(item.children, check) - return item - }) - } + item.checked = check; + item.children = modifyRecursive(item.children, check); + return item; + }); + }; - // Function to map over data and add parent_id + depth property to each item + // Function to map over data and add parent_id + depth property to each item const addCheckedAndParentProperty = ( treeData: { [key: string]: any }[], selectedIds: string[], - parent_id: string = null, - depth = 0, + parent_id: string | null = null, + depth = 0 ) => { if (!Array.isArray(treeData)) { - return + return; } return treeData.map((item: { [key: string]: any } | any) => { const newItem = { ...item, - checked: Boolean(selectedIds && selectedIds.length && selectedIds.includes(item.id)), + checked: Boolean( + selectedIds && selectedIds.length && selectedIds.includes(item.id) + ), parent_id, depth, - } + }; if (newItem.children && newItem.children.length > 0) { const children = item.checked && !returnAllSelected ? modifyRecursive(item.children, true) - : item.children + : item.children; newItem.children = addCheckedAndParentProperty( children, selectedIds, newItem.id, depth + 1 - ) + ); } - return newItem - }) - } + return newItem; + }); + }; useEffect(() => { const formattedData = addCheckedAndParentProperty( treeData, variant === "single" ? [selectedIds?.[0]] : selectedIds - ) + ); - setFormattedData(formattedData) + setFormattedData(formattedData); if (variant === "single") { // No selectedIds, reset state if (selectedIds?.length === 0 || !selectedIds?.length) { - setSingleSelectedItem({ id: [], value: "", item: []}) + setSingleSelectedItem({ id: [], value: "", item: [] }); } else { // If there is a selectedId but no current item, set the selectedItem if (selectedIds?.length !== 0 && !singleSelectedItem.value) { - const selectedItem = filterFormattedDataById(formattedData, selectedIds[0]) + const selectedItem = filterFormattedDataById( + formattedData, + selectedIds[0] + ); if (!selectedItem.length) { - setSingleSelectedItem({ id: [], value: "", item: []}) + setSingleSelectedItem({ id: [], value: "", item: [] }); } else { - const { id, value } = selectedItem[0] - setSingleSelectedItem({ id: [id], value, item: selectedItem}) + const { id, value } = selectedItem[0]; + setSingleSelectedItem({ id: [id], value, item: selectedItem }); } } } } - }, [treeData, selectedIds]) + }, [treeData, selectedIds]); useEffect(() => { if (returnAllSelected) { - setReturnedArray(getCheckedItems(formattedData)) + setReturnedArray(getCheckedItems(formattedData)); } else if (variant === "single") { - setDefaultReturn(singleSelectedItem.item) + setDefaultReturn(singleSelectedItem.item); } else { - setDefaultReturn(getDefaultCheckedItems(formattedData)) + setDefaultReturn(getDefaultCheckedItems(formattedData)); } - }, [formattedData]) + }, [formattedData]); useEffect(() => { // Function to handle clicks outside the dropdown @@ -184,16 +193,14 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => { ) { setIsDropdownClosed(true) } - } + }; // Attach the event listener - window.addEventListener("click", handleClickOutside) + window.addEventListener("click", handleClickOutside); // Clean up the event listener on unmount return () => { - window.removeEventListener("click", handleClickOutside) - } - }, []) - - + window.removeEventListener("click", handleClickOutside); + }; + }, []); // Iterate over tree, find item and set checked or unchecked const modifyValue = ( @@ -202,69 +209,67 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => { check: boolean ) => { if (!Array.isArray(tree)) { - return + return; } return tree.map((item: any) => { - if (item.id != id) item.children = modifyValue(id, item.children, check) + if (item.id != id) item.children = modifyValue(id, item.children, check); else { - item.checked = check + item.checked = check; if (variant === "single") { // Single select: no children should be checked - item.children = modifyRecursive(item.children, !check) + item.children = modifyRecursive(item.children, !check); } else { - item.children = modifyRecursive(item.children, check) + item.children = modifyRecursive(item.children, check); } } - return item - }) - } + return item; + }); + }; // Clone tree, check items + children const checkItem = (item: { [key: string]: any }) => { - const tree = cloneDeep(formattedData) + const tree = cloneDeep(formattedData); if (returnAllSelected) { - return modifyValue(item.id, tree, true) + return modifyValue(item.id, tree, true); } else { - const checkedTree = modifyValue(item.id, tree, true) - return recursiveCheckParent(item, checkedTree) + const checkedTree = modifyValue(item.id, tree, true); + return recursiveCheckParent(item, checkedTree); } - } + }; // Clone tree, uncheck items + children const unCheckItem = (item: { [key: string]: any }) => { - const tree = cloneDeep(formattedData) + const tree = cloneDeep(formattedData); if (returnAllSelected) { - return modifyValue(item.id, tree, false) + return modifyValue(item.id, tree, false); } else { - const uncheckedTree = modifyValue(item.id, tree, false) - return getAncestorsOfUnchecked(uncheckedTree, item) + const uncheckedTree = modifyValue(item.id, tree, false); + return getAncestorsOfUnchecked(uncheckedTree, item); } - } + }; // setFormattedData with proper properties const changeItem = (item: { [key: string]: any }, check: boolean) => { - const tree = check ? checkItem(item) : unCheckItem(item) - setFormattedData(tree) - - return tree - } + const tree = check ? checkItem(item) : unCheckItem(item); + setFormattedData(tree); - + return tree; + }; // Click event for x on form pill const handlePillClose = (event: any, clickedItem: { [key: string]: any }) => { // Prevents the dropdown from closing when clicking on the pill - event.stopPropagation() - const updatedTree = changeItem(clickedItem, false) + event.stopPropagation(); + const updatedTree = changeItem(clickedItem, false); // Logic for removing items from returnArray or defaultReturn when pills clicked if (returnAllSelected) { - onSelect(getCheckedItems(updatedTree)) + onSelect(getCheckedItems(updatedTree)); } else { - onSelect(getDefaultCheckedItems(updatedTree)) + onSelect(getDefaultCheckedItems(updatedTree)); } - } + }; // Handle click on input wrapper(entire div with pills, typeahead, etc) so it doesn't close when input or form pill is clicked const handleInputWrapperClick = (e: any) => { @@ -272,163 +277,114 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => { e.target.id === "multiselect_input" || e.target.classList.contains("pb_form_pill_tag") ) { - return + return; } - setIsDropdownClosed(!isDropdownClosed) - } + setIsDropdownClosed(!isDropdownClosed); + }; // Main function to handle any click inside dropdown const handledropdownItemClick = (e: any, check: boolean) => { - const clickedItem = e.target.parentNode.id + const clickedItem = e.target.parentNode.id; // Setting filterItem to "" will clear textinput and clear typeahead - setFilterItem("") + setFilterItem(""); - const filtered = filterFormattedDataById(formattedData, clickedItem) - const updatedTree = changeItem(filtered[0], check) + const filtered = filterFormattedDataById(formattedData, clickedItem); + const updatedTree = changeItem(filtered[0], check); if (returnAllSelected) { - onSelect(getCheckedItems(updatedTree)) + onSelect(getCheckedItems(updatedTree)); } else { - onSelect(getDefaultCheckedItems(updatedTree)) + onSelect(getDefaultCheckedItems(updatedTree)); } - } + }; // Single select - const handleRadioButtonClick = ( - e: React.ChangeEvent, - ) => { - const { id, value: inputText } = e.target + const handleRadioButtonClick = (e: React.ChangeEvent) => { + const { id, value: inputText } = e.target; // The radio button needs a unique ID, this grabs the ID before the hyphen - const selectedItemID = id.match(/^[^-]*/)[0] + const selectedItemID = id.match(/^[^-]*/)[0]; // Reset tree checked state, triggering useEffect - const treeWithNoSelections = modifyRecursive(formattedData, false) + const treeWithNoSelections = modifyRecursive(formattedData, false); // Update tree with single selection - const treeWithSelectedItem = modifyValue(selectedItemID, treeWithNoSelections, true) - const selectedItem = filterFormattedDataById(treeWithSelectedItem, selectedItemID) - - setFormattedData(treeWithSelectedItem) - setSingleSelectedItem({id: [selectedItemID], value: inputText, item: selectedItem}) + const treeWithSelectedItem = modifyValue( + selectedItemID, + treeWithNoSelections, + true + ); + const selectedItem = filterFormattedDataById( + treeWithSelectedItem, + selectedItemID + ); + + setFormattedData(treeWithSelectedItem); + setSingleSelectedItem({ + id: [selectedItemID], + value: inputText, + item: selectedItem, + }); // Reset the filter to always display dropdown options on click - setFilterItem("") - setIsDropdownClosed(true) + setFilterItem(""); + setIsDropdownClosed(true); - onSelect(selectedItem) + onSelect(selectedItem); }; // Single select: reset the tree state upon typing const handleRadioInputChange = (inputText: string) => { - modifyRecursive(formattedData, false) - setDefaultReturn([]) - setSingleSelectedItem({id: [], value: inputText, item: []}) - setFilterItem(inputText) + modifyRecursive(formattedData, false); + setDefaultReturn([]); + setSingleSelectedItem({ id: [], value: inputText, item: [] }); + setFilterItem(inputText); }; - const isTreeRowExpanded = (item: any) => expanded.indexOf(item.id) > -1 + const isTreeRowExpanded = (item: any) => expanded.indexOf(item.id) > -1; // Handle click on chevron toggles in dropdown const handleToggleClick = (id: string, event: React.MouseEvent) => { - event.stopPropagation() - const clickedItem = filterFormattedDataById(formattedData, id) + event.stopPropagation(); + const clickedItem = filterFormattedDataById(formattedData, id); if (clickedItem) { - let expandedArray = [...expanded] - const itemExpanded = isTreeRowExpanded(clickedItem[0]) + let expandedArray = [...expanded]; + const itemExpanded = isTreeRowExpanded(clickedItem[0]); if (itemExpanded) - expandedArray = expandedArray.filter((i) => i != clickedItem[0].id) - else expandedArray.push(clickedItem[0].id) + expandedArray = expandedArray.filter((i) => i != clickedItem[0].id); + else expandedArray.push(clickedItem[0].id); - setExpanded(expandedArray) + setExpanded(expandedArray); } - } + }; const itemsSelectedLength = () => { - let items + let items; if (returnAllSelected && returnedArray && returnedArray.length) { - items = returnedArray.length + items = returnedArray.length; } else if (!returnAllSelected && defaultReturn && defaultReturn.length) { - items = defaultReturn.length + items = defaultReturn.length; } - return items - } + return items; + }; // Rendering formattedData to UI based on typeahead - const renderNestedOptions = (items: { [key: string]: any }[]) => { - return ( -
    - {Array.isArray(items) && - items.map((item: { [key: string]: any }) => { - return ( -
    -
  • -
    - { !item.parent_id && !item.children ? null : -
    - 0 - ? "" - : "toggle_icon" - } - icon={ - isTreeRowExpanded(item) ? "chevron-down" : "chevron-right" - } - onClick={(event: any) => - handleToggleClick(item.id, event) - } - variant="link" - /> -
    - } - { variant === "single" ? ( - item.hideRadio ? ( - {item.label} - ) : - ) => ( - handleRadioButtonClick(e) - )} - padding={item.children ? 'none' : 'xs'} - type="radio" - value={item.label} - /> - ) : ( - - { - handledropdownItemClick(e, !item.checked) - }} - type="checkbox" - value={item.label} - /> - - )} -
    - {isTreeRowExpanded(item) && - item.children && - item.children.length > 0 && - (variant === "single" || !filterItem) && ( // Show children if expanded is true -
    {renderNestedOptions(item.children)}
    - )} -
  • -
    - ) - })} -
- ) - } + const renderNestedOptions = (items: { [key: string]: string; }[] | any ) => { + const hasOptionsChild = React.Children.toArray(props.children).some( + (child: any) => child.type === MultiLevelSelect.Options + ); + + if (hasOptionsChild) { + return React.Children.map(props.children, (child) => { + if (child.type === MultiLevelSelect.Options) { + return React.cloneElement(child, { items }); + } + return null; + }); + } else { + // If no children, use the default rendering + return ( + + ); + } + }; + return (
{ className={classes} id={id} > -
+
-
@@ -509,15 +473,17 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => { { + onChange={(e) => { variant === "single" ? handleRadioInputChange(e.target.value) - : setFilterItem(e.target.value) + : setFilterItem(e.target.value); }} onClick={() => setIsDropdownClosed(false)} placeholder={ inputDisplay === "none" && itemsSelectedLength() - ? `${itemsSelectedLength()} ${itemsSelectedLength() === 1 ? "item" : "items"} selected` + ? `${itemsSelectedLength()} ${ + itemsSelectedLength() === 1 ? "item" : "items" + } selected` : "Start typing..." } value={singleSelectedItem.value || filterItem} @@ -546,15 +512,16 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
- {renderNestedOptions( - filterItem - ? findByFilter(formattedData, filterItem) - : formattedData - )} + {renderNestedOptions( + filterItem ? findByFilter(formattedData, filterItem) : formattedData + )}
+
- ) -} + ); +}; + +MultiLevelSelect.Options = MultiLevelSelectOptions; -export default MultiLevelSelect \ No newline at end of file +export default MultiLevelSelect; diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/context/index.tsx b/playbook/app/pb_kits/playbook/pb_multi_level_select/context/index.tsx new file mode 100644 index 0000000000..b974b5ef6b --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/context/index.tsx @@ -0,0 +1,5 @@ +import { createContext } from "react"; + +const MultiLevelSelectContext = createContext({}); + +export default MultiLevelSelectContext; \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx index e364900c70..cb2be03263 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx @@ -87,4 +87,4 @@ const MultiLevelSelectDefault = (props) => { ) }; -export default MultiLevelSelectDefault; +export default MultiLevelSelectDefault; \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx new file mode 100644 index 0000000000..8d1f7d4b4a --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx @@ -0,0 +1,105 @@ +import React from "react"; +import MultiLevelSelect from "../_multi_level_select"; +import Badge from "../../pb_badge/_badge"; + +const treeData = [ + { + label: "Power Home Remodeling", + value: "Power Home Remodeling", + id: "powerhome1", + expanded: true, + children: [ + { + label: "People", + value: "People", + id: "people1", + expanded: true, + status: "active", + children: [ + { + label: "Talent Acquisition", + value: "Talent Acquisition", + id: "talent1", + }, + { + label: "Business Affairs", + value: "Business Affairs", + id: "business1", + status: "active", + variant: "primary", + + children: [ + { + label: "Initiatives", + value: "Initiatives", + id: "initiative1", + }, + { + label: "Learning & Development", + value: "Learning & Development", + id: "development1", + status: "Inactive", + }, + ], + }, + { + label: "People Experience", + value: "People Experience", + id: "experience1", + }, + ], + }, + { + label: "Contact Center", + value: "Contact Center", + id: "contact1", + status: "Inactive", + variant: "error", + children: [ + { + label: "Appointment Management", + value: "Appointment Management", + id: "appointment1", + }, + { + label: "Customer Service", + value: "Customer Service", + id: "customer1", + }, + { + label: "Energy", + value: "Energy", + id: "energy1", + }, + ], + }, + ], + }, +]; + +const MultiLevelSelectWithChildren = (props) => { + return ( +
+ + console.log("Selected Items", selectedNodes) + } + treeData={treeData} + {...props} + > + + {(item) => ( + + )} + + +
+ ); +}; + +export default MultiLevelSelectWithChildren; diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.md b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.md new file mode 100644 index 0000000000..d0aeb8d340 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.md @@ -0,0 +1 @@ +The MultiLevelSelect also provides a subcomponent structure which can be used to render children to the right of the Checkboxes and their labels. As seen in the code snippet below, these children have access to the current item being iterated over which can be used for conditional rendering. diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx new file mode 100644 index 0000000000..a6c03f7859 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx @@ -0,0 +1,106 @@ +import React from "react"; +import MultiLevelSelect from "../_multi_level_select"; +import Badge from "../../pb_badge/_badge"; + +const treeData = [ + { + label: "Power Home Remodeling", + value: "Power Home Remodeling", + id: "powerhome1", + expanded: true, + children: [ + { + label: "People", + value: "People", + id: "people1", + expanded: true, + status: "active", + children: [ + { + label: "Talent Acquisition", + value: "Talent Acquisition", + id: "talent1", + }, + { + label: "Business Affairs", + value: "Business Affairs", + id: "business1", + status: "active", + variant: "primary", + + children: [ + { + label: "Initiatives", + value: "Initiatives", + id: "initiative1", + }, + { + label: "Learning & Development", + value: "Learning & Development", + id: "development1", + status: "Inactive", + }, + ], + }, + { + label: "People Experience", + value: "People Experience", + id: "experience1", + }, + ], + }, + { + label: "Contact Center", + value: "Contact Center", + id: "contact1", + status: "Inactive", + variant: "error", + children: [ + { + label: "Appointment Management", + value: "Appointment Management", + id: "appointment1", + }, + { + label: "Customer Service", + value: "Customer Service", + id: "customer1", + }, + { + label: "Energy", + value: "Energy", + id: "energy1", + }, + ], + }, + ], + }, +]; + +const MultiLevelSelectWithChildrenWithRadios = (props) => { + return ( +
+ + console.log("Selected Items", selectedNodes) + } + treeData={treeData} + variant="single" + {...props} + > + + {(item) => ( + + )} + + +
+ ); +}; + +export default MultiLevelSelectWithChildrenWithRadios; diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.md b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.md new file mode 100644 index 0000000000..b4f8fbc2a6 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.md @@ -0,0 +1 @@ +The MultiLevelSelect subcomponent structure is also available in the 'Single Select' variant. In this variant, the children will be rendered to the right of the Radios and their labels. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml index c232dee812..273db8bb68 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml @@ -15,3 +15,6 @@ examples: - multi_level_select_return_all_selected: Return All Selected - multi_level_select_selected_ids_react: Selected Ids - multi_level_select_color: With Pills (Custom Color) + - multi_level_select_with_children: Checkboxes With Children + - multi_level_select_with_children_with_radios: Single Select With Children + diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/index.js b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/index.js index 6b8be4ec17..7c3e1fd0fc 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/index.js @@ -4,3 +4,5 @@ export { default as MultiLevelSelectSingleChildrenOnly } from './_multi_level_se export { default as MultiLevelSelectReturnAllSelected } from './_multi_level_select_return_all_selected.jsx' export { default as MultiLevelSelectSelectedIdsReact } from "./_multi_level_select_selected_ids_react.jsx" export { default as MultiLevelSelectColor } from './_multi_level_select_color.jsx' +export { default as MultiLevelSelectWithChildren } from './_multi_level_select_with_children.jsx' +export { default as MultiLevelSelectWithChildrenWithRadios } from './_multi_level_select_with_children_with_radios.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/multi_level_select_options.tsx b/playbook/app/pb_kits/playbook/pb_multi_level_select/multi_level_select_options.tsx new file mode 100644 index 0000000000..c9a0c4e208 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/multi_level_select_options.tsx @@ -0,0 +1,149 @@ +import React, {useContext} from "react"; +import classnames from "classnames"; +import MultiLevelSelectContext from "./context"; +import { globalProps, GlobalProps } from "../utilities/globalProps"; +import { + buildAriaProps, + buildCss, + buildDataProps, + buildHtmlProps, +} from "../utilities/props"; +import Checkbox from "../pb_checkbox/_checkbox"; +import Radio from "../pb_radio/_radio"; +import CircleIconButton from "../pb_circle_icon_button/_circle_icon_button"; +import Body from "../pb_body/_body"; + +type MultiLevelSelectOptionsProps = { + aria?: { [key: string]: string }, + children?: React.ReactNode | ((item: any) => React.ReactNode), + className?: string, + dark?: boolean, + data?: { [key: string]: string }, + htmlOptions?: {[key: string]: string | number | boolean | (() => void)}, +} & GlobalProps; + +const MultiLevelSelectOptions = ({ + children, + items, + ...props +}: MultiLevelSelectOptionsProps) => { +const { + variant, + inputName, + renderNestedOptions, + isTreeRowExpanded, + handleToggleClick, + handleRadioButtonClick, + handledropdownItemClick, + filterItem, +} = useContext(MultiLevelSelectContext) + +const { + aria = {}, + className, + data = {}, + htmlOptions = {}, +} = props; + +const ariaProps = buildAriaProps(aria); +const dataProps = buildDataProps(data); +const htmlProps = buildHtmlProps(htmlOptions); +const classes = classnames( + buildCss("pb_multi_level_select_options"), + globalProps(props), + className +); + + return ( +
    + {Array.isArray(items) && + items.map((item: { [key: string]: any }) => { + return ( +
    +
  • +
    + {!item.parent_id && !item.children ? null : ( +
    + 0 + ? "" + : "toggle_icon" + } + icon={ + isTreeRowExpanded(item) + ? "chevron-down" + : "chevron-right" + } + onClick={(event: React.MouseEvent) => + handleToggleClick(item.id, event) + } + variant="link" + /> +
    + )} + {variant === "single" ? ( + item.hideRadio ? ( + {item.label} + ) : ( + ) => + handleRadioButtonClick(e) + } + padding={item.children ? "none" : "xs"} + type="radio" + value={item.label} + /> + ) + ) : ( + + { + handledropdownItemClick(e, !item.checked); + }} + type="checkbox" + value={item.label} + /> + + )} + {/* Render children next to the checkbox */} + {children && ( + typeof children === "function" ? children(item) : children + )} +
    + {isTreeRowExpanded(item) && + item.children && + item.children.length > 0 && + (variant === "single" || !filterItem) && ( +
    {renderNestedOptions(item.children)}
    + )} +
  • +
    + ); + })} +
+ ); +}; + +export default MultiLevelSelectOptions; \ No newline at end of file From d9b5807efe18ace170bf2a42bc2bf9d9845c7ce8 Mon Sep 17 00:00:00 2001 From: Ben Langfeld Date: Fri, 4 Oct 2024 15:38:35 -0300 Subject: [PATCH 03/31] More storage for CI image builds (#3767) https://ci.powerapp.cloud/blue/organizations/jenkins/powerhome%2Fplaybook/detail/master/996/pipeline/ --------- Co-authored-by: Thomas Jung --- Jenkinsfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 2dea7c0e79..233dcc2588 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -25,6 +25,9 @@ app.build( memory: '10Gi', ] ], + storageConfig: [ + size: '15Gi', + ], ]) } From 555543ae9026a4c1fb5f2503a4c80136e91e4c6b Mon Sep 17 00:00:00 2001 From: Jasper Furniss Date: Mon, 7 Oct 2024 15:43:33 -0400 Subject: [PATCH 04/31] Encrypted OpenAI Key (#3774) **What does this PR do?** A clear and concise description with your runway ticket url. Encrypted OpenAI key using bin/deployer from the playbook-website folder. **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. --- playbook-website/config/deploy/prs/secrets.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/playbook-website/config/deploy/prs/secrets.yaml b/playbook-website/config/deploy/prs/secrets.yaml index 291e953d37..0ca6d4c76e 100644 --- a/playbook-website/config/deploy/prs/secrets.yaml +++ b/playbook-website/config/deploy/prs/secrets.yaml @@ -1,6 +1,7 @@ appConfig: secretKeyBase: ENC[AES256_GCM,data:ydJ6vKcPVMp9SxRCxgKH3AgAX5JjeoGn7fDKf77XkhODoTZnRHpkyiAezdhFhakCajJFHmaOttWytEVJiMg9DoW111f4AWsNQ+awVztblsCUmT1ZPgZtAGH8H76RKzl3adRy466E3FFoBzQf2FZhP5fdDRSmhzlHWUZdtrfDt/g=,iv:fDmiR4EzqRJ72pl4xLjG5Lv3bw6ijE7hqDHY9HqBcF8=,tag:vzpbilZlEFKlKrt9Rl9zvA==,type:str] sentry_dsn: ENC[AES256_GCM,data:ufQrClZMvpS72OLyRqlO4jisNfnC793ASma61YUOssmQ2gUb/H/U5loD731e4a1fI6dADN66cjQbEnBGwNz0tU58dPMFlR5wSf+bs+JH2Jgn+w==,iv:m3oKFJrByerC7/DPezR0j98Jsolqp8IOm67trL0zJwc=,tag:JAGhc5ts+QBO+DDttXgSVw==,type:str] + open_ai: ENC[AES256_GCM,data:zVXNbSHsux6JriiGW50+nApdnFZ88wO9pqIc/gOPf51zjKYPH8VZ0zNNXyE2McwZdWI7XqcNuUKFQdMZXl9sWZqfNV4cyrVXc4/XwRscv4yzY+PNKlHmCrI+s/rrqrnOkdMrpf68Vud0ifjuA+0c5YVeCWheSuc44S+XufG/ZxjIwFuQlxNn8pQvFsI3gDkc8UF1v+a0+Umh2fms5ybkOoTnEzFI04HlMak=,iv:CbZdq8QY413G8mwRiIstLrMM9tCIpw1J1pd7sGczSds=,tag:bga4kHU2AxqAtymi4EwXUw==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 @@ -16,8 +17,8 @@ sops: azure_kv: [] hc_vault: [] age: [] - lastmodified: "2023-09-12T19:37:14Z" - mac: ENC[AES256_GCM,data:V4LLnTmHRV39R4VNTsQ5Qc2xjjsE0LuSGYJu2W+ERJkwwCcQe1B874xgSttSQs4aiXybjedqLiveHsr42NGuTvjKDXDxXChffp0bdp/eHB22Av8aONEz9Nbqwr4uBI8SvRPRWL0cT5oFVYR18/cVcxYUYhwreCR5hkg0x+xTF4o=,iv:KsKq/7TqDDCKeIUWW+NGJNNypdiBxth8yeIKMOLjq2k=,tag:i9++nJGiPipwXj7H1dimLQ==,type:str] + lastmodified: "2024-10-07T19:35:37Z" + mac: ENC[AES256_GCM,data:dCiQ3NiH/4kqwVYLyk54SY1RzDffQMmbGx5diR2EyvktuqUpcRgx0TpUkMO4calsKwxv7ujffDpuo+CT2eNjt/b5Thuu6VUYqTr76jqeoyDWAZ4m0EA4q9Gz5Q4n1Y4FPEkhBAmaNfcWERV9zDOgTjdbG3IYEdKgKORHpSEUE4E=,iv:Rx966gFBzEdqynjMsaS5H+YiYDQjKm1IXQah8sK2+hk=,tag:YYVxK7SRk5YLkyJ5KdL0/g==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.7.3 From 61fca9a96d192227ec239999b493082afa2db963 Mon Sep 17 00:00:00 2001 From: Jasper Furniss Date: Tue, 8 Oct 2024 11:01:30 -0400 Subject: [PATCH 05/31] Updating Secrets & Git Ignore (#3776) **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. --- .gitignore | 1 + playbook-website/.gitignore | 1 + playbook-website/config/application.rb | 4 ++++ playbook-website/config/deploy/prs/secrets.yaml | 6 +++--- .../config/deploy/templates/deployment.yaml.erb | 5 +++++ playbook-website/config/deploy/templates/secret.yaml.erb | 1 + playbook-website/config/deploy/templates/shell.yaml.erb | 5 +++++ playbook-website/config/initializers/openai.rb | 3 +++ 8 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 playbook-website/config/initializers/openai.rb diff --git a/.gitignore b/.gitignore index 02f42a1b6b..29825090aa 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ node_modules yarn-error.log playbook/stats.json *.dec.* +.env diff --git a/playbook-website/.gitignore b/playbook-website/.gitignore index 7589e0e431..651de6e0c6 100644 --- a/playbook-website/.gitignore +++ b/playbook-website/.gitignore @@ -35,4 +35,5 @@ yarn-debug.log* # Vite uses dotenv and suggests to ignore local-only env files. See # https://vitejs.dev/guide/env-and-mode.html#env-files *.local +.env diff --git a/playbook-website/config/application.rb b/playbook-website/config/application.rb index 786e521774..547d78312c 100644 --- a/playbook-website/config/application.rb +++ b/playbook-website/config/application.rb @@ -38,6 +38,10 @@ class Application < Rails::Application # config.eager_load_paths << Rails.root.join("extras") # Don't generate system test files. + config.generators.system_tests = nil + + openai_api_key = ENV["OPENAI_API_KEY"] + Rails.application.config.openai_api_key = openai_api_key end end diff --git a/playbook-website/config/deploy/prs/secrets.yaml b/playbook-website/config/deploy/prs/secrets.yaml index 0ca6d4c76e..cab0cc21d0 100644 --- a/playbook-website/config/deploy/prs/secrets.yaml +++ b/playbook-website/config/deploy/prs/secrets.yaml @@ -1,7 +1,7 @@ appConfig: secretKeyBase: ENC[AES256_GCM,data:ydJ6vKcPVMp9SxRCxgKH3AgAX5JjeoGn7fDKf77XkhODoTZnRHpkyiAezdhFhakCajJFHmaOttWytEVJiMg9DoW111f4AWsNQ+awVztblsCUmT1ZPgZtAGH8H76RKzl3adRy466E3FFoBzQf2FZhP5fdDRSmhzlHWUZdtrfDt/g=,iv:fDmiR4EzqRJ72pl4xLjG5Lv3bw6ijE7hqDHY9HqBcF8=,tag:vzpbilZlEFKlKrt9Rl9zvA==,type:str] sentry_dsn: ENC[AES256_GCM,data:ufQrClZMvpS72OLyRqlO4jisNfnC793ASma61YUOssmQ2gUb/H/U5loD731e4a1fI6dADN66cjQbEnBGwNz0tU58dPMFlR5wSf+bs+JH2Jgn+w==,iv:m3oKFJrByerC7/DPezR0j98Jsolqp8IOm67trL0zJwc=,tag:JAGhc5ts+QBO+DDttXgSVw==,type:str] - open_ai: ENC[AES256_GCM,data:zVXNbSHsux6JriiGW50+nApdnFZ88wO9pqIc/gOPf51zjKYPH8VZ0zNNXyE2McwZdWI7XqcNuUKFQdMZXl9sWZqfNV4cyrVXc4/XwRscv4yzY+PNKlHmCrI+s/rrqrnOkdMrpf68Vud0ifjuA+0c5YVeCWheSuc44S+XufG/ZxjIwFuQlxNn8pQvFsI3gDkc8UF1v+a0+Umh2fms5ybkOoTnEzFI04HlMak=,iv:CbZdq8QY413G8mwRiIstLrMM9tCIpw1J1pd7sGczSds=,tag:bga4kHU2AxqAtymi4EwXUw==,type:str] + open_ai: ENC[AES256_GCM,data:duAVK9S9gDz3XkzzP7b/mfUuOU+PEGTXXL9e7y8puhs1eJbaCTavKpdWeRJLlGZEDTW3KlyBl/WBcfSRrPEwIX4BOqiiYUin2TwTSyhsGIenUbka05CalBmq1T/H0Js=,iv:GP0Ug3Z0iWP8cpeFOyG6eyuovcfNs1K7/Q7CoNsZXZU=,tag:IH1NqrIksHBgRjpUv4sTLA==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 @@ -17,8 +17,8 @@ sops: azure_kv: [] hc_vault: [] age: [] - lastmodified: "2024-10-07T19:35:37Z" - mac: ENC[AES256_GCM,data:dCiQ3NiH/4kqwVYLyk54SY1RzDffQMmbGx5diR2EyvktuqUpcRgx0TpUkMO4calsKwxv7ujffDpuo+CT2eNjt/b5Thuu6VUYqTr76jqeoyDWAZ4m0EA4q9Gz5Q4n1Y4FPEkhBAmaNfcWERV9zDOgTjdbG3IYEdKgKORHpSEUE4E=,iv:Rx966gFBzEdqynjMsaS5H+YiYDQjKm1IXQah8sK2+hk=,tag:YYVxK7SRk5YLkyJ5KdL0/g==,type:str] + lastmodified: "2024-10-08T14:44:56Z" + mac: ENC[AES256_GCM,data:lABCE3dB+TN7l0SB13j0/pEE++5wYiL7Hr7yC93S7q6ehFXpPQBXHzZnWJnFzW2+Qi774hR5C04jypCWpcrbNr6hb6l8EM27ssp1NJFC/SBGdPaLgUiiuJcHoCGfhF0T4VgaYZc/sATiYCyfvQn9h9W7+XkakCQyD5GVuqXf07c=,iv:gxeGlWZEtVHO68kAtdz7bJCNGzbcanUzqX73lYeDMwo=,tag:5z//KnPmKcWdWhc9yGBLNg==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.7.3 diff --git a/playbook-website/config/deploy/templates/deployment.yaml.erb b/playbook-website/config/deploy/templates/deployment.yaml.erb index 343681a9bf..a3220c1c94 100644 --- a/playbook-website/config/deploy/templates/deployment.yaml.erb +++ b/playbook-website/config/deploy/templates/deployment.yaml.erb @@ -50,6 +50,11 @@ spec: secretKeyRef: name: playbook key: sentry-dsn + - name: OPEN_AI + valueFrom: + secretKeyRef: + name: playbook + key: open-ai livenessProbe: httpGet: path: /health_check/site diff --git a/playbook-website/config/deploy/templates/secret.yaml.erb b/playbook-website/config/deploy/templates/secret.yaml.erb index 77b3f039ca..1110c51c2f 100644 --- a/playbook-website/config/deploy/templates/secret.yaml.erb +++ b/playbook-website/config/deploy/templates/secret.yaml.erb @@ -9,3 +9,4 @@ type: Opaque data: secret-key-base: <%= Base64.encode64(appConfig.fetch("secretKeyBase")).gsub("\n", "").strip %> sentry-dsn: <%= Base64.encode64(appConfig.fetch("sentry_dsn")).gsub("\n", "").strip %> + open-ai: <%= Base64.encode64(appConfig.fetch("open_ai")).gsub("\n", "").strip %> diff --git a/playbook-website/config/deploy/templates/shell.yaml.erb b/playbook-website/config/deploy/templates/shell.yaml.erb index 7f8f4d4717..8305c84094 100644 --- a/playbook-website/config/deploy/templates/shell.yaml.erb +++ b/playbook-website/config/deploy/templates/shell.yaml.erb @@ -41,6 +41,11 @@ template: value: "true" - name: RAILS_LOG_TO_STDOUT value: "true" + - name: OPEN_AI + valueFrom: + secretKeyRef: + name: playbook + key: open-ai resources: limits: cpu: 0.3 diff --git a/playbook-website/config/initializers/openai.rb b/playbook-website/config/initializers/openai.rb new file mode 100644 index 0000000000..457e6095b6 --- /dev/null +++ b/playbook-website/config/initializers/openai.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +Rails.application.config.openai_api_key = ENV["OPENAI_API_KEY"] From e07eeee62779992d23c4592c80a30afe9c012770 Mon Sep 17 00:00:00 2001 From: powerhome-software-apply <125933697+powerhome-software-apply@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:20:36 -0300 Subject: [PATCH 06/31] Setup SOPS --- .sops.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.sops.yaml b/.sops.yaml index f315deb821..ff8b71d1dc 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -6,9 +6,13 @@ creation_rules: - arn: "arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c" - arn: "arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c" role: "arn:aws:iam::205083374951:role/playbook-admins" + - arn: "arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c" + role: "arn:aws:iam::205083374951:role/forever-people" - key_groups: - kms: - arn: "arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41" - arn: "arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41" role: "arn:aws:iam::205083374951:role/playbook-admins" + - arn: "arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41" + role: "arn:aws:iam::205083374951:role/forever-people" From 6f900630911b2b97c9276f0292183ec476714254 Mon Sep 17 00:00:00 2001 From: Ben Langfeld Date: Wed, 9 Oct 2024 11:02:22 -0400 Subject: [PATCH 07/31] Re-encrypt secrets for FVR access (#3783) Updates the deployer image, and re-encrypts the secrets files to grant the `forever-people` ARN access to the secrets. This unlocks FVR's ability to place MySQL user secrets for https://github.com/powerhome/playbook/pull/3778 --- playbook-website/bin/deployer | 2 +- .../config/deploy/production/secrets.yaml | 23 ++++++++++------- .../config/deploy/prs/secrets.yaml | 25 +++++++++++-------- .../config/deploy/staging/secrets.yaml | 23 ++++++++++------- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/playbook-website/bin/deployer b/playbook-website/bin/deployer index 55164ffd8e..03b6590d8d 100755 --- a/playbook-website/bin/deployer +++ b/playbook-website/bin/deployer @@ -7,7 +7,7 @@ if [ -t 0 ]; then AWS_CREDS_MOUNT="--mount type=bind,source=${HOME}/.aws/credentials,destination=/root/.aws/credentials,readonly" fi -DEPLOYER_IMAGE="image-registry.powerapp.cloud/app/deployer:master-467d8015ffc91fc62c347367db792bb6de0eeea8-1439" +DEPLOYER_IMAGE="image-registry.powerapp.cloud/app/deployer:main-559f8a815f07c3e04dc82bf416c403ef80f6eacb-42" DEPLOYER_MOUNTS="${AWS_CREDS_MOUNT} --mount type=bind,source=$(pwd),destination=/app --mount type=bind,source=${HOME}/.kube,destination=/root/.kube" RUN_DEPLOYER="docker run --tty ${INTERACTIVE} ${EXTRA_ARGS} --rm --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY ${DEPLOYER_MOUNTS} ${DEPLOYER_IMAGE}" diff --git a/playbook-website/config/deploy/production/secrets.yaml b/playbook-website/config/deploy/production/secrets.yaml index 8d8b1dd44c..35a7fcf0da 100644 --- a/playbook-website/config/deploy/production/secrets.yaml +++ b/playbook-website/config/deploy/production/secrets.yaml @@ -1,23 +1,28 @@ appConfig: - secretKeyBase: ENC[AES256_GCM,data:utBa4kM3fNPxfLSDALiJvOz6KVlzSefm/K3xDLrKQYypDY2TbreTY5QpeOH6l49D9zMD5i8XGwn3p9f6OEDg4uwzWLSUw0c6U7EcQkrxmRpvOLiOKz2SX8B3xWv3rdLwNs50LHINvjQcjdJviAo9R/oxLxEs6Ds/snSxYXbKQcs=,iv:p0zTY4f3gXqTLJRSjdhX+WsO2/eA6d/rh3S38O4XvuE=,tag:QjENfqpSSZ4hlvvoAuzRJA==,type:str] - sentry_dsn: ENC[AES256_GCM,data:p1TU1BvZqZyqo3WPVhGQKqT5IB9zsdA/LkEltdp2zNir3Xub7+yKWg5PXVhjCkDii90MPXSEd08zZYUHZQsTdWI6A/f/jMj1DepJhhPq7H8hpQ==,iv:NHG0oirvwkU2dRh9K0eyEjxyA8WAXRXt9HCGyOmKkrg=,tag:kDJqiCE+n5XoSJMVYqbFBQ==,type:str] + secretKeyBase: ENC[AES256_GCM,data:pTc2zofEnukd275BCaIpHOM5LokYsaYdgyGPHH6tggclP8Fj2S3/s7UREfDDuNZdAWAelmfWj3AY6xhtmdNRXt0F1UDvrNw0aCDAUqQK/jfHvgCtPQ2ncdZdpoUeEzmTaHUu3JVCphrCBFWxYrSvQg5qfx/docSiGR/PtaWTgOA=,iv:NwUXrRidkh5gT0E90h8CH9qtddboQnd2uvQfPXVkfM0=,tag:vJ5vPuSFeiRc+kDX5tY3Jg==,type:str] + sentry_dsn: ENC[AES256_GCM,data:pnb9JEfwZ0wCe+eh1CChiKiYAyyMeRtMvJEy9HyY9HFyleFB336Q6nVFx1D74LSxgNbrZiBqg/aA1ZysYCIDRciN6LOMlFAn5AAbMhpYcdNbLQ==,iv:j8l3ISTxrLVF21VRTDkqcmxeMt8jPrZDzDW9rHZ84lc=,tag:eMnHnxYQ+uAIhba0bDNuAA==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c - created_at: "2023-09-12T17:54:03Z" - enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgGYWcyxf9B5fC0BIXbcdcWHAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMTtG/4IC1DRbS0sO0AgEQgDtkAetmvS1Gc4T4SBgXkXzEtZ11EIGuPKbxG4IHpv/Lf1taPKmf02QDhxEiMWCc57jkeWwCSLapvSTIQQ== + created_at: "2024-10-09T14:15:47Z" + enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgE1qwTrUkCGbpiPQPZ2Wy+lAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM5uYzVUOAppdO0PKyAgEQgDtfwSqTGPBcAQB/DMjvu5ePLTHSR+Yb8KhkFICf598s5H4VonhIO7zQROhfUurkPSXK9omJ2WMGuULDiQ== aws_profile: "" - arn: arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c role: arn:aws:iam::205083374951:role/playbook-admins - created_at: "2023-09-12T17:54:03Z" - enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgEAsmw6mIADb67KveUG6MUfAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMkfv8wA9Ec5IztTyEAgEQgDuXn1EN5zG2dP7pJpU4nE/KiJ1krq+UizveI0mSylzpKmRz0BFPtpj6kNv/pANo+YwCNKm5uVOGSkBfRw== + created_at: "2024-10-09T14:15:47Z" + enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgEzvPVSKC0gdgIl7GsNe51uAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMJfl/u6aJQu7MHZ5XAgEQgDu7iEdG6TL87qMoOGrGyEK1jcRq12uyCzOyAGhqkcE7XaWFuqP6ukaU2J12ZHVqAGHQNyhdqGN3sGQSOw== + aws_profile: "" + - arn: arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c + role: arn:aws:iam::205083374951:role/forever-people + created_at: "2024-10-09T14:15:47Z" + enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgF0x/5dkX78lPXUwfrdoJ7JAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMX2NSksmyhVS1j4q5AgEQgDsx23n3n0AtKWp7logPrzOrVwBMD8zhzC3QJyV6iDk7ia4Wk0OubJd84MPo4thMo1HPd0Xjhy4oXBTjUw== aws_profile: "" gcp_kms: [] azure_kv: [] hc_vault: [] age: [] - lastmodified: "2023-09-12T19:35:17Z" - mac: ENC[AES256_GCM,data:GBtmDnvvc+KwYkBML0M8+e02FgpfbY68FfCGSHxMUuAvjPYLHLZVA99pMtsKsFWdSiwRsZLcU6xoJm3y9YRLSGSJ0/7NzdVM5386tuSSm+RdqdcYNYU9hrcf1XhmMDT/enyjYsUP9p5f6sAPorQgj9VHv/1yV6ppZ5SRfrNr8v4=,iv:l7Wt0uqtrEeIAa9CWB7HPpnPJPPbYoAYzD7jIzw3/ao=,tag:ooQCNoR0QdBYsv/tOiLRqA==,type:str] + lastmodified: "2024-10-09T14:15:48Z" + mac: ENC[AES256_GCM,data:8Rq2gIcrWSNzsI79Lo/UOU5wZnyPG/AwODq2K6mYlPwBK0BhkAxKKMmMBCJzBNMWEFX33loqXnPjLE2Y6Cx8WSuI7EvISIzv6ekoRR5n8QnCOarH8zz/DZOnjgQI9F+fn0kRUEMIdyIduiDjAafXyp6RDlHUl7JxxSKaObFQoL0=,iv:xGadGENEQ4RE1GKuJTh0xeZ8hlWr7dSvwYHGTUIcTeM=,tag:r0j68SWB1Fl82iH8KjK0zw==,type:str] pgp: [] unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/playbook-website/config/deploy/prs/secrets.yaml b/playbook-website/config/deploy/prs/secrets.yaml index cab0cc21d0..db8589ca50 100644 --- a/playbook-website/config/deploy/prs/secrets.yaml +++ b/playbook-website/config/deploy/prs/secrets.yaml @@ -1,24 +1,29 @@ appConfig: - secretKeyBase: ENC[AES256_GCM,data:ydJ6vKcPVMp9SxRCxgKH3AgAX5JjeoGn7fDKf77XkhODoTZnRHpkyiAezdhFhakCajJFHmaOttWytEVJiMg9DoW111f4AWsNQ+awVztblsCUmT1ZPgZtAGH8H76RKzl3adRy466E3FFoBzQf2FZhP5fdDRSmhzlHWUZdtrfDt/g=,iv:fDmiR4EzqRJ72pl4xLjG5Lv3bw6ijE7hqDHY9HqBcF8=,tag:vzpbilZlEFKlKrt9Rl9zvA==,type:str] - sentry_dsn: ENC[AES256_GCM,data:ufQrClZMvpS72OLyRqlO4jisNfnC793ASma61YUOssmQ2gUb/H/U5loD731e4a1fI6dADN66cjQbEnBGwNz0tU58dPMFlR5wSf+bs+JH2Jgn+w==,iv:m3oKFJrByerC7/DPezR0j98Jsolqp8IOm67trL0zJwc=,tag:JAGhc5ts+QBO+DDttXgSVw==,type:str] - open_ai: ENC[AES256_GCM,data:duAVK9S9gDz3XkzzP7b/mfUuOU+PEGTXXL9e7y8puhs1eJbaCTavKpdWeRJLlGZEDTW3KlyBl/WBcfSRrPEwIX4BOqiiYUin2TwTSyhsGIenUbka05CalBmq1T/H0Js=,iv:GP0Ug3Z0iWP8cpeFOyG6eyuovcfNs1K7/Q7CoNsZXZU=,tag:IH1NqrIksHBgRjpUv4sTLA==,type:str] + secretKeyBase: ENC[AES256_GCM,data:tX9TTqw+teUH3ugGOXIfCMSE1fofM8YBfei2cXLB8Wbmsxp+bsoIgByMtgAbyHzov73P2PaJhQWWo/fG2W7Zj8T0gQ5BcK9mVFUkbt/+SmQpbbprLeIpauN4Gar2QWzyzJPIVEUopf3gdEERx3a+2rbMotJ4rB8Xfvv9OO4sOy4=,iv:H7nM7+ERPsNpehyuaAXSotrG8YCMhL1VSNAmFggs+Q4=,tag:eE+MuzvnCuQ6XKAKMMkrsA==,type:str] + sentry_dsn: ENC[AES256_GCM,data:9k5OGZqUcLote0IAJt7GJBsDxN3iJxoVtfPn9RKYfV1KZzNNWyeTZKC/PmjdjKotNbXzLPUSIPbtV8bk2DmttOwQr/r0xbnaTcRT2CWh/4XwVA==,iv:qML16zZMGBSO9nz7kSihlvn5uLj1s+RDSSqOb1jivGw=,tag:XvsrAuIAgAboayg2ZN1KZQ==,type:str] + open_ai: ENC[AES256_GCM,data:ewKDrouP0ZuMNAr19DvqBIzYdRInruWH7GxPX2ZH79DVJkT53qLFqzQMIBScqai40WXQmFLNicLnnbHVK+Xcv0tq/xBHtS7BANIwa5XJnWo56jYR3mOdbAcw+ZNFrks=,iv:c5veWRGGydflJ38TFf56ei9GI0tCnE24TLxJGFHscdE=,tag:rGLKDJesBs5JTNt83dsmoA==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 - created_at: "2023-09-12T17:53:37Z" - enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgHC1WOVOEbBuNn+YEN+OKiFAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMGQ04cBdxcLtN+PMRAgEQgDvbxveKjEU0mIfKAh7I3yjnnrUSDl1UymkOJYlz4MaKw+MNze2Q1zIX0FzkwSFLFcW/7NguN5/FQ1thZw== + created_at: "2024-10-09T14:15:23Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgFd2W8ic6/TkyA8p8wcv5IGAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMPj5cEbt+ndJRHdEMAgEQgDuThOL+h28iSGmeB/TcKDZZ61OXEzoWxD3Io9pC63eWkHhJjcQJO/Cklequ8L43LZh9ISXUhWBdm7Nlbg== aws_profile: "" - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 role: arn:aws:iam::205083374951:role/playbook-admins - created_at: "2023-09-12T17:53:37Z" - enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgHs+bIDMZJ9K5YoWkyxqXpxAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMv1q+HlWt0cTzINo0AgEQgDuDojVpcg6NuTpVs8Q2ojw9MnIGfuQ4OI14tvSE78863Esk0Tvgno/oqxDjSfWqIV0kmV/h5hapIm/C3w== + created_at: "2024-10-09T14:15:23Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgFTilLgQ0cpyV0/bCSqiidWAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMHJQrlVXmV3auRC97AgEQgDuvJdg6V6f/H15ui2BVgII+a7sUe4I3Suwcjy/Jl4o9+E7GKETkWm8RLpWdwI0yP65GmjLRkmoqnGERlg== + aws_profile: "" + - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 + role: arn:aws:iam::205083374951:role/forever-people + created_at: "2024-10-09T14:15:23Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgFNq+jUXbUie4tTLRqUg0niAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMBG8uY6UVCCS+9t0lAgEQgDsURce9Oc/zXJFhLivk3rJP4KZRsUrc5qB/6z43P0c3IXvNN3PtTLl+/yPGpZ1zmukv4aBBU0GuVOh0Bw== aws_profile: "" gcp_kms: [] azure_kv: [] hc_vault: [] age: [] - lastmodified: "2024-10-08T14:44:56Z" - mac: ENC[AES256_GCM,data:lABCE3dB+TN7l0SB13j0/pEE++5wYiL7Hr7yC93S7q6ehFXpPQBXHzZnWJnFzW2+Qi774hR5C04jypCWpcrbNr6hb6l8EM27ssp1NJFC/SBGdPaLgUiiuJcHoCGfhF0T4VgaYZc/sATiYCyfvQn9h9W7+XkakCQyD5GVuqXf07c=,iv:gxeGlWZEtVHO68kAtdz7bJCNGzbcanUzqX73lYeDMwo=,tag:5z//KnPmKcWdWhc9yGBLNg==,type:str] + lastmodified: "2024-10-09T14:15:23Z" + mac: ENC[AES256_GCM,data:Sm8hMCHoRGSVa+0/QbBWk60xkl0vn2pHk061liryW8OyAw7brVakAndBgReYeVlks1OS+fuKfc4RsoU3qJWBStJyffxNS5vbXWCugW+HnmbTNK3jBO++Ij2X8qXceWVYQnNXR3N0BTYx9YdyriGNtbtF6jz6PSZN3pUBcpbgLn8=,iv:2tg6O2Tg+o7myXbYlhd/36y1d25fB3h0UuxWXQZWv4Y=,tag:0wJnXb4pXZiUYQWdZSr2ng==,type:str] pgp: [] unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/playbook-website/config/deploy/staging/secrets.yaml b/playbook-website/config/deploy/staging/secrets.yaml index ae5aa11956..02c18a3628 100644 --- a/playbook-website/config/deploy/staging/secrets.yaml +++ b/playbook-website/config/deploy/staging/secrets.yaml @@ -1,23 +1,28 @@ appConfig: - secretKeyBase: ENC[AES256_GCM,data:T9rr+yNvQIiOKYgGO8p2MM+iqDius270ooqlHTwNDMMXrApWsybNBLIy88tupU/FDm01/FI7ggUZXKbO7WJvBFyxnY/nAQFbw5Xv58lsEGjIrIWAfwANd96NuCOjahnG6PRnICyLbxenQfK7gjXzdxNzRWeRhGFHp/hj29JVx9k=,iv:wB2eCtbFsCXXrPgCOUj2abpBdgsq0iOxL8AiN7wL/Hs=,tag:B3ZaXCT2IlcLmKhR/XrQ4Q==,type:str] - sentry_dsn: ENC[AES256_GCM,data:mZ68vE4jCVVi+xrOBnhldUwvObAIdo4HzAXg/r7J+x0BdYTH7SMQWL70mNdVyjbW+KA6aYq6VY6kOIa4FgcUfgBP6cmNDoE4SwTSosUTBm0r3Q==,iv:vqtZKpp7ALuQJYrkDpgn+sjvmEMq9tzBVlc/2hcj8qE=,tag:JByaTyGLemz1Z0wuqzjVBg==,type:str] + secretKeyBase: ENC[AES256_GCM,data:kOsJ3LgAPCPZ4Vdm/EBFksqneV7tSeFqKrI965UEqkZ7EQ7n/STuE0vte90/oMF1C0eBYzwHEsnT/1kPirKn5/Rz4np2M1jQIvucTfk7QMd76fueIS2lXtLw155rYL8iEh5JyxviOJNfL93U3FiySo1Fa8+1FoOqvlnHa4+/Uoo=,iv:pjEQHF499TYjNEe+O95G3VgqpAfUyJPtVws8nl3zw4k=,tag:38L8NgqyGQu8ueIBS8NcmQ==,type:str] + sentry_dsn: ENC[AES256_GCM,data:8vuODktXtDvYOUKS5XR7JJ0t9sX0urMWdmIeu66wwvqfGNJogacXj097PqM4MHLIsl0DICJen/2xA+uvUSV+ZVqJH7gCqeY3fH4XwMhE8dKZZA==,iv:5NYs+MiDZiR41cuazIFPXC3IKJsJgbl4eoRvTym0dTo=,tag:XP1rGTjYcl5nMUScJ/3w/A==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 - created_at: "2023-09-12T17:53:51Z" - enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgFAHaaoyZIhorapEiPc+qICAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMX8BKJ5TiL9Pi1CvvAgEQgDuKgTNkmLECrXQsOPMINqkAT5HzdOqKY/7QT3wS6+EcgVF/kVSD++0yceqYZY6urOFZs9AcPoffFghlGw== + created_at: "2024-10-09T14:15:36Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgF5FR6Zmbb2LmX/Ok1UTBQ1AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMGDGnVWMmvb8v8r8JAgEQgDt6so3JsL9WSkXPw1VsgKL0K1S5G0J1of4jczsf7uJp4lCWyzYzN3iK0aD/ZMDQfq/CHkAo3da9NodoZg== aws_profile: "" - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 role: arn:aws:iam::205083374951:role/playbook-admins - created_at: "2023-09-12T17:53:51Z" - enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgEkSuHel9uABP7kWsy3YHNXAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMjUBIc+aCDRrVDqTOAgEQgDu8ZcejevO0RMIsndJbpN47yZ/kCIhfsLGv7YQ2CamhWhAP5b4V3Bz7aY+bFkXEmsumaSbQB2j+e+vL5Q== + created_at: "2024-10-09T14:15:36Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgE7Au4PrldyBvUsxJu2XM0WAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM6DF1DaKLw/6qWGHAAgEQgDsjGaEv60fzlf3o7oHNGPanagVIHk7eNWBzpgjAH256rJoC7mXeSn2qFEnRjLYOA1/4LeLgXD3B0+bHAQ== + aws_profile: "" + - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 + role: arn:aws:iam::205083374951:role/forever-people + created_at: "2024-10-09T14:15:36Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgG3HMs993vA2J7ym2Gaf0jXAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMg1PVXgdv92RavP2NAgEQgDvx4LuwYEmnAGtCR1j8B0DhkL0sIowwUrJgQKcpf3453ixUzxs+vnILsUajTxm3RVpWxoPV1OjNxRfLew== aws_profile: "" gcp_kms: [] azure_kv: [] hc_vault: [] age: [] - lastmodified: "2023-09-12T19:36:52Z" - mac: ENC[AES256_GCM,data:7fTUBII1iCtdGe4QZFkXw5j+P2vUhRJF6Ny4CxeTzZctRi+upYME23maBPpzEFjKU0J9S1jLl1XFodOSZe5Sc3E1UEOBhs1/J7rvuEhWZC5QkKXVrq/UzNkDPO9eTQmE7XA6/w0TLx++xcy9iSHIsrLubidAU3EO5R4eaiaT3gw=,iv:zfDYlma2ltcRHWw47rn/kSJjJX3s95Xg5kvBM96Koz4=,tag:GiHRN/m9Q6GAITAFmPZobg==,type:str] + lastmodified: "2024-10-09T14:15:37Z" + mac: ENC[AES256_GCM,data:XOHOpmOsuCjeaB+85g3SyDw5FtA+PakIGJURGMQhKXQtT/OHjAgepUDhloI8b92RntAOIRqD1GhB5lwHpfNUk0ZVVIQ/uwwwIOuQWZLhq3c+V+ItcPyCQgF2GCy9yTxaiyNABBDpGRPeLFr+kP2R2Yri9mROhuHaNmkdCp1ftbc=,iv:iRlk5I0cdM5H42YKnsgpg3F9/rs1lvWy6y8TIXKvhqU=,tag:7JSWiFdFTGyvXBMuvd0XMw==,type:str] pgp: [] unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 From 6fcfb7e103ea8029804c35cee7c14a3d92505e53 Mon Sep 17 00:00:00 2001 From: Aaron Kuehler Date: Wed, 9 Oct 2024 11:50:41 -0400 Subject: [PATCH 08/31] Fix staging and production deployments (#3782) Deployments to non-pr environments are broken. - https://milano.powerapp.cloud/powerhome/playbook/staging/deploys/9880200# ``` key not found: "open_ai" ``` First, https://github.com/powerhome/playbook/pull/3776 only added values for the `open_ai` value to the `prs` environment - IE review stacks. The project still needs valaues for staging and production. Second, even if these values were present, the env var name the application is looking for and the env var name used by the deployment are different. https://github.com/powerhome/playbook/blob/ddf4db1da92383ac58e8c6f17669a0d38629c7c2/playbook-website/config/initializers/openai.rb#L3 --------- Co-authored-by: Jasper --- playbook-website/config/deploy/production/secrets.yaml | 5 +++-- playbook-website/config/deploy/staging/secrets.yaml | 5 +++-- playbook-website/config/deploy/templates/deployment.yaml.erb | 4 ++-- playbook-website/config/deploy/templates/shell.yaml.erb | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/playbook-website/config/deploy/production/secrets.yaml b/playbook-website/config/deploy/production/secrets.yaml index 35a7fcf0da..50ecb25332 100644 --- a/playbook-website/config/deploy/production/secrets.yaml +++ b/playbook-website/config/deploy/production/secrets.yaml @@ -1,6 +1,7 @@ appConfig: secretKeyBase: ENC[AES256_GCM,data:pTc2zofEnukd275BCaIpHOM5LokYsaYdgyGPHH6tggclP8Fj2S3/s7UREfDDuNZdAWAelmfWj3AY6xhtmdNRXt0F1UDvrNw0aCDAUqQK/jfHvgCtPQ2ncdZdpoUeEzmTaHUu3JVCphrCBFWxYrSvQg5qfx/docSiGR/PtaWTgOA=,iv:NwUXrRidkh5gT0E90h8CH9qtddboQnd2uvQfPXVkfM0=,tag:vJ5vPuSFeiRc+kDX5tY3Jg==,type:str] sentry_dsn: ENC[AES256_GCM,data:pnb9JEfwZ0wCe+eh1CChiKiYAyyMeRtMvJEy9HyY9HFyleFB336Q6nVFx1D74LSxgNbrZiBqg/aA1ZysYCIDRciN6LOMlFAn5AAbMhpYcdNbLQ==,iv:j8l3ISTxrLVF21VRTDkqcmxeMt8jPrZDzDW9rHZ84lc=,tag:eMnHnxYQ+uAIhba0bDNuAA==,type:str] + open_ai: ENC[AES256_GCM,data:He7ruscMUMlif05pZhA7f0/rjVt1oG8qzJKGXO6SxHVKZ5t062dD3covgJAGqjCh9aH4wcjCS2xyhBNJWbqjcLhrYsLHuZB4URLfA1CyhJa+maReOCOH88vTutFdIYY=,iv:cZBzbIWxtp+sq3mcE+YGeHeALlSADsU7uXgmKgbmQc8=,tag:a09iJDiJRQ5ImhndwPeYkA==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c @@ -21,8 +22,8 @@ sops: azure_kv: [] hc_vault: [] age: [] - lastmodified: "2024-10-09T14:15:48Z" - mac: ENC[AES256_GCM,data:8Rq2gIcrWSNzsI79Lo/UOU5wZnyPG/AwODq2K6mYlPwBK0BhkAxKKMmMBCJzBNMWEFX33loqXnPjLE2Y6Cx8WSuI7EvISIzv6ekoRR5n8QnCOarH8zz/DZOnjgQI9F+fn0kRUEMIdyIduiDjAafXyp6RDlHUl7JxxSKaObFQoL0=,iv:xGadGENEQ4RE1GKuJTh0xeZ8hlWr7dSvwYHGTUIcTeM=,tag:r0j68SWB1Fl82iH8KjK0zw==,type:str] + lastmodified: "2024-10-09T15:15:57Z" + mac: ENC[AES256_GCM,data:VXYQZHkMLlAppSm5tEbLSEVJtjaPzgW3ePN8tzvoxyIsGe/zr8QkuP7EcXbPdk3HxOwcSh+X0TfF/AOi2cZHChQYH6I69JgbXuYp5dHRWymGe9TIF0GbaChyWK1tbfSadty9BOqb6CtzwD/tMF59G8BvLElqekpTf71h2IaFC8k=,iv:7wbclLhQrBJJ8+M8lGJSUr6Td4YjSYitX4AUmZmp7XE=,tag:jbFgriGrENcoKHxR5jqC5w==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 diff --git a/playbook-website/config/deploy/staging/secrets.yaml b/playbook-website/config/deploy/staging/secrets.yaml index 02c18a3628..9365badf28 100644 --- a/playbook-website/config/deploy/staging/secrets.yaml +++ b/playbook-website/config/deploy/staging/secrets.yaml @@ -1,6 +1,7 @@ appConfig: secretKeyBase: ENC[AES256_GCM,data:kOsJ3LgAPCPZ4Vdm/EBFksqneV7tSeFqKrI965UEqkZ7EQ7n/STuE0vte90/oMF1C0eBYzwHEsnT/1kPirKn5/Rz4np2M1jQIvucTfk7QMd76fueIS2lXtLw155rYL8iEh5JyxviOJNfL93U3FiySo1Fa8+1FoOqvlnHa4+/Uoo=,iv:pjEQHF499TYjNEe+O95G3VgqpAfUyJPtVws8nl3zw4k=,tag:38L8NgqyGQu8ueIBS8NcmQ==,type:str] sentry_dsn: ENC[AES256_GCM,data:8vuODktXtDvYOUKS5XR7JJ0t9sX0urMWdmIeu66wwvqfGNJogacXj097PqM4MHLIsl0DICJen/2xA+uvUSV+ZVqJH7gCqeY3fH4XwMhE8dKZZA==,iv:5NYs+MiDZiR41cuazIFPXC3IKJsJgbl4eoRvTym0dTo=,tag:XP1rGTjYcl5nMUScJ/3w/A==,type:str] + open_ai: ENC[AES256_GCM,data:XX6uvmMSXUvaZFcLR4o5zOmZaQDodlRP4tpZ9Od73pnk4sA6VEaPoqBk3AHr0Ret5KD0WhE2TLnMt2UVEv4+xeRcct0lJtN/n+xXODXsGXoRYRRF7hiYGTJJewT5glY=,iv:tOWyS9Ep+TmP4MtY6LEez9XcD/mWERVV2sbEXQU8rrc=,tag:v3sv3+FzybiKijYY7ncqYg==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 @@ -21,8 +22,8 @@ sops: azure_kv: [] hc_vault: [] age: [] - lastmodified: "2024-10-09T14:15:37Z" - mac: ENC[AES256_GCM,data:XOHOpmOsuCjeaB+85g3SyDw5FtA+PakIGJURGMQhKXQtT/OHjAgepUDhloI8b92RntAOIRqD1GhB5lwHpfNUk0ZVVIQ/uwwwIOuQWZLhq3c+V+ItcPyCQgF2GCy9yTxaiyNABBDpGRPeLFr+kP2R2Yri9mROhuHaNmkdCp1ftbc=,iv:iRlk5I0cdM5H42YKnsgpg3F9/rs1lvWy6y8TIXKvhqU=,tag:7JSWiFdFTGyvXBMuvd0XMw==,type:str] + lastmodified: "2024-10-09T15:15:44Z" + mac: ENC[AES256_GCM,data:zU8gmGa8a6HK0pC9Vn8za0W33frXOcQwSpfGJg0XVE2688PvAIp1D7yHPGS/ncZ56geVr9AGAlScwiAnhG1SVwUPUEi2ryfRCvyrlXwHlwvVP1/bmehmF7MP6jbfj4r6XRjdcKI6fTpkPsNdZ7718VAqxh8VFNNcuHU9NoXGTBM=,iv:sgQStIeMoF15XjH6FgOKIng5nqOhYeeqRAu7AeyrVz4=,tag:coDyxtBzfaTR5ERuC3Azbw==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 diff --git a/playbook-website/config/deploy/templates/deployment.yaml.erb b/playbook-website/config/deploy/templates/deployment.yaml.erb index a3220c1c94..c1396a2d99 100644 --- a/playbook-website/config/deploy/templates/deployment.yaml.erb +++ b/playbook-website/config/deploy/templates/deployment.yaml.erb @@ -50,7 +50,7 @@ spec: secretKeyRef: name: playbook key: sentry-dsn - - name: OPEN_AI + - name: OPEN_AI_KEY valueFrom: secretKeyRef: name: playbook @@ -77,4 +77,4 @@ spec: "memory" => "256Mi", "ephemeral-storage" => "100Mi" } - } %> + } %> diff --git a/playbook-website/config/deploy/templates/shell.yaml.erb b/playbook-website/config/deploy/templates/shell.yaml.erb index 8305c84094..6c4344a74b 100644 --- a/playbook-website/config/deploy/templates/shell.yaml.erb +++ b/playbook-website/config/deploy/templates/shell.yaml.erb @@ -41,7 +41,7 @@ template: value: "true" - name: RAILS_LOG_TO_STDOUT value: "true" - - name: OPEN_AI + - name: OPENAI_API_KEY valueFrom: secretKeyRef: name: playbook From c9d28b847e450176763d42b883292538a46042d3 Mon Sep 17 00:00:00 2001 From: Elisa Shapiro <83474365+ElisaShapiro@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:16:24 -0400 Subject: [PATCH 09/31] [PBNTR-442] Advanced Table kit "toggle all" is inverted in Rails (#3790) **What does this PR do?** A clear and concise description with your runway ticket url. [PBNTR-442](https://runway.powerhrg.com/backlog_items/PBNTR-442) address the current incorrect behavior for the "toggle all" icon in the Rails Advanced Table kit. For both headers (see [default doc ex](https://playbook.powerapp.cloud/kits/advanced_table/rails?sort=year_asc#default-required-props)) and subheaders (see [subheader doc ex](https://playbook.powerapp.cloud/kits/advanced_table/rails#subrow-headers)) the toggle all button will just switch the rows that are closed to open and vice versa. The desired behavior is to open all rows and close them in sync - if only some are open, the first click should open all of them, and from then on close all/open all in sync (see [React doc examples](https://playbook.powerapp.cloud/kits/advanced_table/react#default-required-props) for expected behavior here). This PR updates the PbEnhancedElement logic (and moves script tag javascript from two kit html.erb files to the index.js). **Screenshots:** Screenshots to visualize your addition/change https://github.com/user-attachments/assets/7d280ee3-373a-42aa-98cf-dc056f2dc913 **How to test?** Steps to confirm the desired behavior: 1. Go to the rails advanced table [default doc example](https://pr3790.playbook.beta.gm.powerapp.cloud/kits/advanced_table/rails#default-required-props). 2. Click on one of the years to expand that row. Then click on the toggle all icon next to the "Year" label. This should open the other two years so all three are open. Click on the toggle all icon again and all three year sections should close. 3. Keep clicking around the doc example - behavior should be consistent no matter which rows start open/closed. 4. Go to the rails advanced table [subrow headers doc example](https://pr3790.playbook.beta.gm.powerapp.cloud/kits/advanced_table/rails#subrow-headers). 5. Click on one of the years to expand that row, click on the Q1 section to open it further, then click on one of the months in the nested section. Then click on the toggle all icon of the subrow header next to the "Month" label. This should open the other month so both are open. Click on the toggle all icon again and both month sections should close. 6. Keep clicking around the doc example - behavior should be consistent no matter which rows start open/closed. #### 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_advanced_table/index.js | 60 +++++++++++++++++++ .../pb_advanced_table/table_header.html.erb | 10 +--- .../table_subrow_header.html.erb | 10 +--- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/index.js b/playbook/app/pb_kits/playbook/pb_advanced_table/index.js index fa6e2523bf..ec9c1a71b5 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/index.js +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/index.js @@ -13,9 +13,20 @@ export default class PbAdvancedTable extends PbEnhancedElement { get target() { return document.querySelector(CONTENT_SELECTOR.replace("id", this.element.id)) } + + static expandedRows = new Set() + static isCollapsing = false connect() { this.element.addEventListener('click', () => { + if (!PbAdvancedTable.isCollapsing) { + const isExpanded = this.element.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block' + if (!isExpanded) { + PbAdvancedTable.expandedRows.add(this.element.id) + } else { + PbAdvancedTable.expandedRows.delete(this.element.id) + } + } this.toggleElement(this.target) }) } @@ -75,4 +86,53 @@ export default class PbAdvancedTable extends PbEnhancedElement { this.element.querySelector(UP_ARROW_SELECTOR).style.display = 'inline-block' this.element.querySelector(DOWN_ARROW_SELECTOR).style.display = 'none' } + + static handleToggleAllHeaders(element) { + const table = element.closest('.pb_table') + const firstLevelButtons = table.querySelectorAll('.pb_advanced_table_body > .pb_table_tr [data-advanced-table]') + + const expandedRows = Array.from(firstLevelButtons).filter(button => + button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block' + ) + + if (expandedRows.length === firstLevelButtons.length) { + expandedRows.forEach(button => { + button.click() + }) + this.expandedRows.clear() + } else { + firstLevelButtons.forEach(button => { + if (!this.expandedRows.has(button.id)) { + button.click() + } + }) + } + } + static handleToggleAllSubRows(element, rowDepth) { + const parentElement = element.closest(".toggle-content") + const subrowButtons = parentElement.querySelectorAll('.depth-sub-row-' + rowDepth + ' [data-advanced-table]') + + const expandedSubRows = Array.from(subrowButtons).filter(button => + button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block' + ) + + if (expandedSubRows.length === subrowButtons.length) { + expandedSubRows.forEach(button => { + button.click() + }) + } else { + subrowButtons.forEach(button => { + if (!this.expandedRows.has(button.id)) { + button.click() + } + }) + } + } +} + +window.expandAllRows = (element) => { + PbAdvancedTable.handleToggleAllHeaders(element) } +window.expandAllSubRows = (element, rowDepth) => { + PbAdvancedTable.handleToggleAllSubRows(element, rowDepth) +} \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb b/playbook/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb index f98b8bfda3..fa857e1926 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb @@ -13,12 +13,4 @@ <% end %> <% end %> <% end %> -<% end %> - - \ No newline at end of file +<% end %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb b/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb index 8870a1f9ea..8815af0bf0 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb @@ -23,12 +23,4 @@ <% end %> <% end %> <% end %> -<% end %> - - \ No newline at end of file +<% end %> \ No newline at end of file From 9ccb2f7f6ee63ed72e9388e910d39dac14434297 Mon Sep 17 00:00:00 2001 From: Nida Ghuman Date: Tue, 15 Oct 2024 14:28:44 -0400 Subject: [PATCH 10/31] [PBNTR-606] Add Reset Functionality to Rails side of MultiLevelSelect (#3792) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Runway Story](https://runway.powerhrg.com/backlog_items/PBNTR-606) This PR: - ✅ Adds reset functionality to rails side of kit so it can be used within nitro_search_playbook_filter in Nitro - ✅ Unblocks [this work](https://runway.powerhrg.com/backlog_items/FIRST-1537) for First Contact - ✅ Creates reset doc example on rails side to showcase how to use exposed function --- .../_multi_level_select.tsx | 17 ++++ .../docs/_multi_level_select_reset.html.erb | 93 +++++++++++++++++++ .../docs/_multi_level_select_reset.md | 1 + .../pb_multi_level_select/docs/example.yml | 1 + 4 files changed, 112 insertions(+) create mode 100644 playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb create mode 100644 playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.md diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx b/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx index 07cd852766..bb1c92e1f7 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx @@ -202,6 +202,23 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => { }; }, []); + useEffect(() => { + if (id) { + // Attach the clear function to the window, scoped by the id + (window as any)[`clearMultiLevelSelect_${id}`] = () => { + const resetData = modifyRecursive(formattedData, false); + setFormattedData(resetData); + setReturnedArray([]); + setDefaultReturn([]); + setSingleSelectedItem({ id: [], value: "", item: [] }); + onSelect([]); + }; + return () => { + delete (window as any)[`clearMultiLevelSelect_${id}`]; + }; + } + }, [formattedData, id, onSelect]); + // Iterate over tree, find item and set checked or unchecked const modifyValue = ( id: string, diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb new file mode 100644 index 0000000000..c8959a350d --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb @@ -0,0 +1,93 @@ +<% treeData = [{ + label: "Power Home Remodeling", + value: "Power Home Remodeling", + id: "100", + expanded: true, + children: [ + { + label: "People", + value: "People", + id: "101", + expanded: true, + children: [ + { + label: "Talent Acquisition", + value: "Talent Acquisition", + id: "102", + }, + { + label: "Business Affairs", + value: "Business Affairs", + id: "103", + children: [ + { + label: "Initiatives", + value: "Initiatives", + id: "104", + }, + { + label: "Learning & Development", + value: "Learning & Development", + id: "105", + }, + ], + }, + { + label: "People Experience", + value: "People Experience", + id: "106", + }, + ], + }, + { + label: "Contact Center", + value: "Contact Center", + id: "107", + children: [ + { + label: "Appointment Management", + value: "Appointment Management", + id: "108", + }, + { + label: "Customer Service", + value: "Customer Service", + id: "109", + }, + { + label: "Energy", + value: "Energy", + id: "110", + }, + ], + }, + ], +}] %> + +<%= pb_rails("multi_level_select", props: { + id: "multi-level-select-reset-example", + name: "my_array", + tree_data: treeData, + return_all_selected: true +}) %> + +<%= pb_rails("button", props: { text: "Reset", margin_top: "lg", id:"multilevelselect-reset-button" }) %> + + + \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.md b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.md new file mode 100644 index 0000000000..bfe15aa20f --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.md @@ -0,0 +1 @@ +In order to clear the multilevelselect selection using an external trigger (like a reset button), the `clearMultiLevelSelect` function can be used. See the code snippet below to see this in action. The function is scoped by id so an id MUST be used on the multilevelselect kit and passed to the function as shown for it to work. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml index 273db8bb68..09f50945e4 100644 --- a/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml @@ -7,6 +7,7 @@ examples: - multi_level_select_selected_ids: Selected Ids - multi_level_select_with_form: With Form - multi_level_select_color: With Pills (Custom Color) + - multi_level_select_reset: Reset Selection react: - multi_level_select_default: Default From 88302fb76475937ac9c57acccab95be273f436e3 Mon Sep 17 00:00:00 2001 From: Elisa Shapiro <83474365+ElisaShapiro@users.noreply.github.com> Date: Wed, 16 Oct 2024 10:24:25 -0400 Subject: [PATCH 11/31] [PBNTR-600] Filter Kit - Filter Kit displaying zero results (#3799) **What does this PR do?** A clear and concise description with your runway ticket url. [PBNTR-600](https://runway.powerhrg.com/backlog_items/PBNTR-600) improves the React Filter kit's UI so it more closely matches the Rails side. Specifically, this code update allows for "Results: 0" to appear in the context of the filter kit if there are no results for a filtered search; previously, the "Results: " TitleCount component would just disappear if there were no Results. If the Filter's results prop is undefined, the "Results: " TitleCount component will not appear, which is logical behavior. **Screenshots:** Screenshots to visualize your addition/change _Screenshots on their way_ Current production behavior: Results 1+ shows "Results: 1" (first filter) but Results: 0 does not appear (second filter) react filter doc ex current if 1 or 0 New behavior for default template Filter: "Results: 0" appears react filter shows 0 New behavior for single template Filter - 0 Results and XXX Results both appear now 0 or multiple results in single template If Results prop is undefined, "Results: " will not appear undefined results in both templates Screenshot from an alpha in nitro-web on local - Results: 0 now appears on our React Basic Table Template filter local alpha showing react results 0 in templates **How to test?** Steps to confirm the desired behavior: 1. Go to review env [react filter kit default doc example](https://pr3799.playbook.beta.gm.powerapp.cloud/kits/filter/react#default)- see "Results: 0" in second filter in example. 2. Compare to current React Filter kit implementation - can test by going to [React Basic Table template](https://nitroqa.powerhrg.com/dev_docs/playbook/templates/demos/basic_table/react) and searching something that will have no results like "first name=elisashapiro". Observe how the "Results: " section just disappears. 3. Compare to Rails Filter kit behavior in current production Playbook [Rails default doc example](https://playbook.powerapp.cloud/kits/filter/rails#default) and the [Rails Basic Table template](https://nitroqa.powerhrg.com/dev_docs/playbook/templates/demos/basic_table/rails) - "Results: 0" appears for in both places. #### 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.~~ --- .../app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx | 6 ++++-- .../app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/playbook/app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx b/playbook/app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx index 922324ed08..de66e3ed24 100644 --- a/playbook/app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx +++ b/playbook/app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx @@ -13,6 +13,7 @@ type ResultsCountProps = { const ResultsCount = ({ dark, results, title }: ResultsCountProps): React.ReactElement => { const resultTitle = () => { + if (results == null) return null return ( { + if (results == null) return null return ( { - if (results && title) { + if (results != null && results >=0 && title) { return ( <> {resultTitle()} ) - } else if (results) { + } else if (results !=null && results >=0 ) { return ( <> {justResults()} diff --git a/playbook/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx b/playbook/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx index b670c057a5..f57bfa097d 100644 --- a/playbook/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +++ b/playbook/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx @@ -78,7 +78,7 @@ const FilterDefault = (props) => { double minWidth="375px" onSortChange={SortingChangeCallback} - results={1} + results={0} sortOptions={{ popularity: 'Popularity', // eslint-disable-next-line From 86b841a15d3e0cb3b7f6921ee321099f64ee1992 Mon Sep 17 00:00:00 2001 From: Carlos Lima Date: Thu, 17 Oct 2024 09:10:16 -0300 Subject: [PATCH 12/31] [PBNTR-568] Adding React Dropdown cleaning feature (#3793) **What does this PR do?** Adding React Dropdown cleaning feature **Screenshots:** image **How to test?** 1. Go to the Dropdown kit page 2. Scroll down to the Clear Selection 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. - [ ] **TESTS** I have added test coverage to my code. --- .../playbook/pb_dropdown/_dropdown.tsx | 24 ++++++++-- .../docs/_dropdown_clear_selection.jsx | 45 +++++++++++++++++++ .../docs/_dropdown_clear_selection.md | 1 + .../playbook/pb_dropdown/docs/example.yml | 1 + .../playbook/pb_dropdown/docs/index.js | 1 + .../subcomponents/DropdownOption.tsx | 2 +- .../subcomponents/DropdownTrigger.tsx | 4 +- 7 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx create mode 100644 playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.md diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx index 5940e22e29..27234c3fec 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +++ b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from "react"; +import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from "react"; import classnames from "classnames"; import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props"; import { globalProps } from "../utilities/globalProps"; @@ -38,7 +38,14 @@ type DropdownProps = { triggerRef?: any; }; -const Dropdown = (props: DropdownProps) => { +interface DropdownComponent + extends React.ForwardRefExoticComponent> { + Option: typeof DropdownOption; + Trigger: typeof DropdownTrigger; + Container: typeof DropdownContainer; +} + +const Dropdown = forwardRef((props: DropdownProps, ref: any) => { const { aria = {}, autocomplete = false, @@ -125,7 +132,7 @@ const Dropdown = (props: DropdownProps) => { const filteredOptions = optionsWithBlankSelection?.filter((option: GenericObject) => { const label = typeof option.label === 'string' ? option.label.toLowerCase() : option.label; return String(label).toLowerCase().includes(filterItem.toLowerCase()); - }); + }); // For keyboard accessibility: Set focus within dropdown to selected item if it exists useEffect(() => { @@ -175,6 +182,14 @@ const Dropdown = (props: DropdownProps) => { dark }); + useImperativeHandle(ref, () => ({ + clearSelected: () => { + setSelected({}); + setFilterItem(""); + setIsDropDownClosed(true); + onSelect && onSelect(null); + }, + })); return (
{
) -}; +}) as DropdownComponent +Dropdown.displayName = "Dropdown"; Dropdown.Option = DropdownOption; Dropdown.Trigger = DropdownTrigger; Dropdown.Container = DropdownContainer; diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx new file mode 100644 index 0000000000..eeece1ec52 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx @@ -0,0 +1,45 @@ +import React, { useRef } from 'react' +import { Button, Dropdown } from 'playbook-ui' + +const options = [ + { + label: "United States", + value: "United States", + }, + { + label: "Canada", + value: "Canada", + }, + { + label: "Pakistan", + value: "Pakistan", + } +] + +const DropdownClearSelection = (props) => { + const dropdownRef = useRef(null) + + const handleReset = () => { + if (dropdownRef.current) { + dropdownRef.current.clearSelected() + } + } + + return ( + <> + +