diff --git a/Dockerfile b/Dockerfile index 39db9352c6..c147dee5c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -90,3 +90,7 @@ COPY --link --from=release /home/app/src/playbook-website/public /home/app/src/p COPY --link --from=release /home/app/src/node_modules/@powerhome/playbook-icons/icons /home/app/src/temp-icons RUN cp -r /home/app/src/temp-icons/* /home/app/src/playbook-website/app/javascript/images/ RUN rm -rf /home/app/src/temp-icons + +COPY --link --from=release /home/app/src/node_modules/@powerhome/playbook-icons/aliases.json /home/app/src/aliases.json +RUN cp /home/app/src/aliases.json /home/app/src/playbook-website/app/javascript/aliases.json +RUN rm /home/app/src/aliases.json diff --git a/playbook-website/app/controllers/pages_controller.rb b/playbook-website/app/controllers/pages_controller.rb index b4bf4423b5..4450acdd1b 100755 --- a/playbook-website/app/controllers/pages_controller.rb +++ b/playbook-website/app/controllers/pages_controller.rb @@ -240,7 +240,12 @@ def set_category def kit_categories @category = params[:category] - aggregate_kits.find { |item| item["category"] == @category }["components"].map { |component| component["name"] } + components = aggregate_kits.find { |item| item["category"] == @category }["components"] + filter_kits_by_status(components, status: "beta").map { |component| component["name"] } + end + + def filter_kits_by_status(components, status: nil) + components.reject { |component| status && component["status"] == status } end def set_kit diff --git a/playbook-website/app/javascript/components/Website/src/components/CategoryTitle/index.tsx b/playbook-website/app/javascript/components/Website/src/components/CategoryTitle/index.tsx index 056757cac9..e148e7ea52 100644 --- a/playbook-website/app/javascript/components/Website/src/components/CategoryTitle/index.tsx +++ b/playbook-website/app/javascript/components/Website/src/components/CategoryTitle/index.tsx @@ -6,13 +6,14 @@ import { linkFormat } from "../../../../../utilities/website_sidebar_helper"; import "./styles.scss"; type CategoryTitleProps = { - name: string; + category: string; }; -export const CategoryTitle = ({ name }: CategoryTitleProps): React.ReactElement => { +export const CategoryTitle = ({ category }: CategoryTitleProps): React.ReactElement => { + return ( - + <Title size={{ xs: 3, sm: 2, md: 2, lg: 2, xl: 2 }} tag="h1" text={linkFormat(category)} /> <Icon className="icon mobile" icon="circle-arrow-right" size="sm" /> <Icon className="icon desktop" icon="circle-arrow-right" size="xl" /> </Flex> diff --git a/playbook-website/app/javascript/components/Website/src/hooks/loaders.ts b/playbook-website/app/javascript/components/Website/src/hooks/loaders.ts index 427e0cf1b4..0c4a25c360 100644 --- a/playbook-website/app/javascript/components/Website/src/hooks/loaders.ts +++ b/playbook-website/app/javascript/components/Website/src/hooks/loaders.ts @@ -7,13 +7,14 @@ interface ComponentTypes { } interface CategoryTypes { - name: string; + category: string; description: string; components: ComponentTypes[]; } -const sortByName = (a: ComponentTypes, b: ComponentTypes) => - a.name.localeCompare(b.name); +const sortByName = (a: ComponentTypes, b: ComponentTypes): number => { + return a.name.localeCompare(b.name); +} const sortComponentsByName = (kitCategory: CategoryTypes) => { kitCategory.components.sort(sortByName); @@ -22,8 +23,8 @@ const sortComponentsByName = (kitCategory: CategoryTypes) => { export const ComponentsLoader: () => Promise<CategoryTypes[]> = async () => { const response = await fetch("/beta/kits.json"); const data = await response.json(); - - data.kits.sort(sortByName).forEach(sortComponentsByName); + + data.kits.forEach(sortComponentsByName); return data; }; @@ -40,9 +41,9 @@ export const CategoryLoader: ( const response = await fetch("/beta/kits.json"); const { kits } = await response.json(); - const filteredData = kits.filter( - (kit: ComponentTypes) => kit.name === params.name - )[0]; + const filteredData = kits.find( + (kit: CategoryTypes) => kit.category === params.category + ); filteredData.components.sort(sortByName); diff --git a/playbook-website/app/javascript/components/Website/src/pages/CategoryShow/index.tsx b/playbook-website/app/javascript/components/Website/src/pages/CategoryShow/index.tsx index 60debf4886..b41c78fcf1 100644 --- a/playbook-website/app/javascript/components/Website/src/pages/CategoryShow/index.tsx +++ b/playbook-website/app/javascript/components/Website/src/pages/CategoryShow/index.tsx @@ -13,13 +13,13 @@ import { Kit } from "../ComponentList" import "./styles.scss" export default function CategoryShow() { - const { components, name, description } = useLoaderData() + const { components, category, description } = useLoaderData() const [kitsToShow, setKitsToShow] = useState(components) const [platform, setPlatform] = useState('react') return ( <> - <Hero description={description} title={linkFormat(name)} /> + <Hero description={description} title={linkFormat(category)} /> <Flex align="center" @@ -39,7 +39,7 @@ export default function CategoryShow() { <Body className="previous-route" color="light">Components</Body> </NavLink> <Icon className="category-breadcrumb-icon" icon="angle-right" /> - <Body text={linkFormat(name)} /> + <Body text={linkFormat(category)} /> </Flex> {!kitsToShow.length && ( @@ -54,14 +54,17 @@ export default function CategoryShow() { )} <KitGrid> - {kitsToShow.map(({ description, name }: Kit, index: number) => ( - <KitCard - description={description} - name={name} - key={`category-${name}-${index}`} - platform={platform} - /> - ))} + {kitsToShow.filter(component => component.status === "stable") + .map(({ description, name }: Kit, index: number) => { + return( + <KitCard + description={description} + name={name} + key={`category-${name}-${index}`} + platform={platform} + /> + ) + })} </KitGrid> </PageContainer> </> diff --git a/playbook-website/app/javascript/components/Website/src/pages/ComponentList.tsx b/playbook-website/app/javascript/components/Website/src/pages/ComponentList.tsx index a1e876cb9b..2e8089d830 100644 --- a/playbook-website/app/javascript/components/Website/src/pages/ComponentList.tsx +++ b/playbook-website/app/javascript/components/Website/src/pages/ComponentList.tsx @@ -10,7 +10,7 @@ import { PageContainer } from "../components/PageContainer" import { CategoryTitle } from "../components/CategoryTitle" export type Kit = { - name: string; + category: string; components: { name: string; description: string; @@ -65,14 +65,14 @@ export default function ComponentList() { /> </Flex> ) : ( - kitsToShow.map(({ name, components }: Kit, index: number) => ( + kitsToShow.map(({ category, components }: Kit, index: number) => ( <section className="category mb_xl" - key={`${name}-${index}`} - id={name} + key={`${category}-${index}`} + id={category} > - <NavLink to={`/beta/kit_category/${name}`}> - <CategoryTitle name={name} /> + <NavLink to={`/beta/kit_category/${category}`}> + <CategoryTitle category={category} /> </NavLink> <KitGrid> {components diff --git a/playbook-website/app/javascript/components/Website/src/pages/IconList/index.tsx b/playbook-website/app/javascript/components/Website/src/pages/IconList/index.tsx index 149015f4f7..179a4ee8f7 100644 --- a/playbook-website/app/javascript/components/Website/src/pages/IconList/index.tsx +++ b/playbook-website/app/javascript/components/Website/src/pages/IconList/index.tsx @@ -237,12 +237,54 @@ export default function IconList() { </Flex> + <Flex + justify='center' + marginX={{ lg: "sm", xl: "sm" }} + paddingLeft="xs" + paddingTop="sm" + > + <FlexItem alignSelf='stretch' maxWidth='xxl' flexGrow={1}> + <Title paddingBottom="sm" size={3} tag='h3' text='Other Props' /> + </FlexItem> + </Flex> + <Flex + justify='center' + marginX={{ lg: "sm", xl: "sm" }} + paddingLeft="xs" + > + <FlexItem alignSelf='stretch' maxWidth='xxl' flexGrow={1}> + <Flex> + <Card + marginRight='sm' + hover={{ shadow: "deeper" }} + cursor='pointer' + > + <Icon icon="roofing" size="2x" pull="left" /> + </Card> + <Card + marginRight='sm' + hover={{ shadow: "deeper" }} + cursor='pointer' + > + <Icon icon="roofing" size="2x" pull="right" /> + </Card> + <Card + marginRight='sm' + hover={{ shadow: "deeper" }} + cursor='pointer' + > + <Icon icon="roofing" size="2x" fixedWidth /> + </Card> + </Flex> + </FlexItem> + </Flex> <Flex justify='center' marginX={{ lg: "sm", xl: "sm" }} paddingLeft="xs" + paddingTop="sm" > <FlexItem alignSelf='stretch' maxWidth='xxl' flexGrow={1}> <Title paddingBottom="sm" size={3} tag='h3' text='Color' /> diff --git a/playbook-website/app/javascript/packs/app.js b/playbook-website/app/javascript/packs/app.js index 112cf28094..234bc0e9ff 100644 --- a/playbook-website/app/javascript/packs/app.js +++ b/playbook-website/app/javascript/packs/app.js @@ -40,7 +40,7 @@ const router = createBrowserRouter( <Route element={<CategoryShow />} loader={CategoryLoader} - path="kit_category/:name" + path="kit_category/:category" /> <Route element={<IconList />} diff --git a/playbook-website/app/views/samples/icons.html.erb b/playbook-website/app/views/samples/icons.html.erb index b217d32568..1e1a191729 100644 --- a/playbook-website/app/views/samples/icons.html.erb +++ b/playbook-website/app/views/samples/icons.html.erb @@ -28,7 +28,7 @@ <% end %> <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> <%= pb_rails("card/card_body", props: { padding: "md", }) do %> - <%= pb_rails("icon", props: { icon: "nitro" }) %> + <%= pb_rails("icon", props: { icon: "nitro-n" }) %> <% end %> <% end %> <% end %> @@ -92,6 +92,39 @@ <% end %> <% end %> +<%= pb_rails("flex", props: { padding_left: "md"} ) do %> + <%= pb_rails("flex/flex_item") do %> + <%= pb_rails("title", props: {size: 3, text: "Pull/Width", padding_bottom: "sm"})%> + <% end %> +<% end %> +<%= pb_rails("flex", props: { padding_left: "md" }) do %> + <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> + <%= pb_rails("card/card_body", props: { padding: "md" }) do %> + <%= pb_rails("icon", props: { icon: "powergon-p", size: "2x", pull: "right" }) %> + <% end %> + <% end %> + <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> + <%= pb_rails("card/card_body", props: { padding: "md" }) do %> + <%= pb_rails("icon", props: { icon: "powergon-p", size: "2x", pull: "left" }) %> + <% end %> + <% end %> + <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> + <%= pb_rails("card/card_body", props: { padding: "md" }) do %> + <%= pb_rails("icon", props: { icon: "powergon-p", size: "2x", fixed_width: true }) %> + <% end %> + <% end %> + <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> + <%= pb_rails("card/card_body", props: { padding: "md" }) do %> + <%= pb_rails("icon", props: { icon: "powergon-p", size: "2x", fixed_width: false }) %> + <% end %> + <% end %> + <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> + <%= pb_rails("card/card_body", props: { padding: "md" }) do %> + <%= pb_rails("icon", props: { icon: "powergon-p", size: "2x", border: true }) %> + <% end %> + <% end %> +<% end %> + <%= pb_rails("flex", props: { padding_left: "md"} ) do %> <%= pb_rails("flex/flex_item") do %> <%= pb_rails("title", props: {size: 3, text: "Color", padding_bottom: "sm"})%> @@ -161,3 +194,33 @@ <% end %> <% end %> <% end %> + + +<%= pb_rails("flex", props: { padding_left: "md"} ) do %> + <%= pb_rails("flex/flex_item") do %> + <%= pb_rails("title", props: {size: 3, text: "Aliases", padding_bottom: "sm"})%> + <% end %> +<% end %> + +<%= pb_rails("flex", props: { padding_left: "md" }) do %> + <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> + <%= pb_rails("card/card_body", props: { padding: "md" }) do %> + <%= pb_rails("icon", props: { icon: "house", size: "2x", spin: true }) %> + <% end %> + <% end %> + <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> + <%= pb_rails("card/card_body", props: { padding: "md" }) do %> + <%= pb_rails("icon", props: { icon: "home", size: "2x", pulse: true }) %> + <% end %> + <% end %> + <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> + <%= pb_rails("card/card_body", props: { padding: "md" }) do %> + <%= pb_rails("icon", props: { icon: "gear", size: "2x", spin: true }) %> + <% end %> + <% end %> + <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %> + <%= pb_rails("card/card_body", props: { padding: "md" }) do %> + <%= pb_rails("icon", props: { icon: "cog", size: "2x", pulse: true }) %> + <% end %> + <% end %> +<% end %> diff --git a/playbook-website/config/application.rb b/playbook-website/config/application.rb index 48972cf51a..786e521774 100644 --- a/playbook-website/config/application.rb +++ b/playbook-website/config/application.rb @@ -28,6 +28,7 @@ class Application < Rails::Application config.load_defaults 7.0 config.icon_path = Rails.env.production? ? "app/javascript/images/" : "../node_modules/@powerhome/playbook-icons/icons" + config.icon_alias_path = Rails.env.production? ? "app/javascript/aliases.json" : "../node_modules/@powerhome/playbook-icons/aliases.json" # Configuration for the application, engines, and railties goes here. # # These settings can be overridden in specific environments using the files diff --git a/playbook-website/package.json b/playbook-website/package.json index 5df7c1f442..900bf7c368 100644 --- a/playbook-website/package.json +++ b/playbook-website/package.json @@ -10,8 +10,8 @@ "dependencies": { "@fortawesome/fontawesome-pro": "6.2.1", "@mapbox/mapbox-gl-draw": "^1.4.1", - "@powerhome/playbook-icons": "0.0.1-alpha.25", - "@powerhome/playbook-icons-react": "0.0.1-alpha.25", + "@powerhome/playbook-icons": "0.0.1-alpha.29", + "@powerhome/playbook-icons-react": "0.0.1-alpha.29", "@powerhome/power-fonts": "0.0.1-alpha.6", "@rails/webpacker": "5.4.3", "@svgr/webpack": "5.5.0", diff --git a/playbook-website/test/menu_yml_spec.rb b/playbook-website/test/menu_yml_spec.rb new file mode 100644 index 0000000000..0ee066d7e4 --- /dev/null +++ b/playbook-website/test/menu_yml_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "yaml" + +yaml_file_path = File.expand_path("../../../playbook/playbook-website/config/menu.yml", __dir__) + +RSpec.describe "Menu YAML File" do + let(:data) { YAML.safe_load(File.read(yaml_file_path), aliases: true) } + + it "should load YAML file without errors" do + expect(data).to_not be_nil + end + + it "should have categories defined" do + expect(data).to have_key("kits") + expect(data["kits"]).to be_an(Array) + expect(data["kits"]).to_not be_empty + end + + it "should have components defined for each category" do + data["kits"].each do |kit| + expect(kit).to have_key("category") + expect(kit["category"]).to be_a(String) + expect(kit).to have_key("components") + expect(kit["components"]).to be_an(Array) + expect(kit["components"]).to_not be_empty + + kit["components"].each do |component| + expect(component).to have_key("name") + expect(component["name"]).to be_a(String) + expect(component).to have_key("platforms") + expect(component["platforms"]).to be_an(Array) + expect(component["platforms"]).to_not be_empty + end + end + end +end diff --git a/playbook/CHANGELOG.md b/playbook/CHANGELOG.md index 77fae07d76..87f143e490 100644 --- a/playbook/CHANGELOG.md +++ b/playbook/CHANGELOG.md @@ -1,3 +1,34 @@ +# 🐉 Effortless Drag and Drop Functionality with the NEW Draggable Kit! ⬆️⬇️ +##### July 10, 2024 + +![13-32-0](https://github.com/user-attachments/assets/c93a0344-ef0a-4fd2-be48-62fb5bf95f1e) + + +We are excited to introduce the new Draggable kit, a highly flexible solution for all your drag-and-drop needs! Use the simple subcomponent structure for greater flexibility or use the customized solutions for the List, Selectable List or Card kits for super easy implementation out of the box! + +[13.32.0](https://github.com/powerhome/playbook/tree/13.32.0) full list of changes: + +**Kit Enhancements:** +- Implementing Dropdown Form Validation [\#3507](https://github.com/powerhome/playbook/pull/3507) ([carloslimasd](https://github.com/carloslimasd)) + +**Fixed Bugs:** +- Remove Duplicate "block" Button in RTE on Start [\#3501](https://github.com/powerhome/playbook/pull/3501) ([kangaree](https://github.com/kangaree)) +- Hide Beta Versions of Kits within Beta Index (with menu.yml) [\#3500](https://github.com/powerhome/playbook/pull/3500) ([elisashapiro](https://github.com/elisashapiro)) +- Date Year Stacked Left Align Text Color Bug [\#3508](https://github.com/powerhome/playbook/pull/3508) ([anthonymig88](https://github.com/anthonymig88)) + +**Improvements:** +- Power Centra in Playbook Part 2 (Change Variable Settings) [\#3134](https://github.com/powerhome/playbook/pull/3134) ([jasoncypret](https://github.com/jasoncypret)) +- Add Category to Playbook Kit Generator [\#3488](https://github.com/powerhome/playbook/pull/3488) ([elisashapiro](https://github.com/elisashapiro)) +- Bump ci-kubed v8.2.0 and Convert to use Remote buildkit cache mounts [\#3413](https://github.com/powerhome/playbook/pull/3413) ([TeamTeaTime](https://github.com/TeamTeaTime)) + +**New Kits:** +- Draggable Kit [\#3487](https://github.com/powerhome/playbook/pull/3487) ([nidaqg](https://github.com/nidaqg)) + + +[Full Changelog](https://github.com/powerhome/playbook/compare/13.31.0...13.32.0) + + + # 🌁 Introducing the New Overlay Kit — A Seamless Way to Implement Overlays! ‍🌁 ##### June 28, 2024 diff --git a/playbook/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_custom.md b/playbook/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_custom.md index 7e320bedc3..0ddf91dd04 100644 --- a/playbook/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_custom.md +++ b/playbook/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_custom.md @@ -1,2 +1,6 @@ The `customOptions` prop provides comprehensive access to additional [Highcharts options](https://api.highcharts.com/highcharts/) that are not explicitly defined as props. It's important to note that certain options may require specific script imports to function properly. + +Note: If you are having trouble getting any Highcharts options to work, please match the formatting of our [staticOptions](https://github.com/powerhome/playbook/blob/master/playbook/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx#L85-L141). For example, `yAxis` will need to be wrapped with square brackets. + +You may also need to override any of the [defaults](https://github.com/powerhome/playbook/blob/master/playbook/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx#L45-L73) in order to get that options to work. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_collapsible/__snapshots__/collapsible.test.js.snap b/playbook/app/pb_kits/playbook/pb_collapsible/__snapshots__/collapsible.test.js.snap index 1d9a7763c9..a18573ba67 100644 --- a/playbook/app/pb_kits/playbook/pb_collapsible/__snapshots__/collapsible.test.js.snap +++ b/playbook/app/pb_kits/playbook/pb_collapsible/__snapshots__/collapsible.test.js.snap @@ -28,7 +28,7 @@ exports[`html structure is correct 1`] = ` style="vertical-align: middle; color: rgb(193, 205, 214);" > <i - class="pb_icon_kit far fa-fw fa-lg fa-chevron-down" + class="pb_icon_kit far fa-lg fa-fw fa-lg fa-chevron-down" /> <span aria-label="chevron-down icon" diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx index 99b1347bc3..5940e22e29 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +++ b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx @@ -22,10 +22,12 @@ import { type DropdownProps = { aria?: { [key: string]: string }; autocomplete?: boolean; + blankSelection?: string; children?: React.ReactChild[] | React.ReactChild | React.ReactElement[]; className?: string; dark?: boolean; data?: { [key: string]: string }; + defaultValue?: GenericObject; error?: string; htmlOptions?: { [key: string]: string | number | boolean | (() => void) }, id?: string; @@ -40,10 +42,12 @@ const Dropdown = (props: DropdownProps) => { const { aria = {}, autocomplete = false, + blankSelection = '', children, className, dark = false, data = {}, + defaultValue = {}, error, htmlOptions = {}, id, @@ -66,7 +70,7 @@ const Dropdown = (props: DropdownProps) => { const [isDropDownClosed, setIsDropDownClosed, toggleDropdown] = useDropdown(isClosed); const [filterItem, setFilterItem] = useState(""); - const [selected, setSelected] = useState<GenericObject>({}); + const [selected, setSelected] = useState<GenericObject>(defaultValue); const [isInputFocused, setIsInputFocused] = useState(false); const [hasTriggerSubcomponent, setHasTriggerSubcomponent] = useState(true); const [hasContainerSubcomponent, setHasContainerSubcomponent] = @@ -116,11 +120,12 @@ const Dropdown = (props: DropdownProps) => { setIsDropDownClosed(isClosed) }, [isClosed]) - const filteredOptions = options?.filter((option: GenericObject) => { + const blankSelectionOption: GenericObject = blankSelection ? [{ label: blankSelection, value: "" }] : []; + const optionsWithBlankSelection = blankSelectionOption.concat(options); + 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(() => { @@ -194,7 +199,7 @@ const Dropdown = (props: DropdownProps) => { inputWrapperRef, isDropDownClosed, isInputFocused, - options, + optionsWithBlankSelection, selected, setFocusedOptionIndex, setIsDropDownClosed, @@ -233,8 +238,8 @@ const Dropdown = (props: DropdownProps) => { <> <DropdownTrigger /> <DropdownContainer> - {options && - options?.map((option: GenericObject) => ( + {optionsWithBlankSelection && + optionsWithBlankSelection?.map((option: GenericObject) => ( <Dropdown.Option key={option.id} option={option} /> diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection.html.erb b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection.html.erb new file mode 100644 index 0000000000..e3405df84a --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection.html.erb @@ -0,0 +1,10 @@ +<% + options = [ + { label: 'United States', value: 'United States', id: 'us' }, + { label: 'Canada', value: 'Canada', id: 'ca' }, + { label: 'Pakistan', value: 'Pakistan', id: 'pk' }, + ] + +%> + +<%= pb_rails("dropdown", props: { blank_selection: "Select One...", options: options }) %> diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection.jsx b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection.jsx new file mode 100644 index 0000000000..a73a5e8c6a --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection.jsx @@ -0,0 +1,31 @@ +import React from 'react' +import { Dropdown } from '../../' + +const DropdownBlankSelection = (props) => { + const options = [ + { + label: "United States", + value: "United States", + }, + { + label: "Canada", + value: "Canada", + }, + { + label: "Pakistan", + value: "Pakistan", + } + ]; + + return ( + <> + <Dropdown + blankSelection="Select one..." + options={options} + {...props} + /> + </> + ) +} + +export default DropdownBlankSelection diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_value.html.erb b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_value.html.erb new file mode 100644 index 0000000000..766ffb8250 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_value.html.erb @@ -0,0 +1,10 @@ +<% + options = [ + { label: 'United States', value: 'United States', id: 'us' }, + { label: 'Canada', value: 'Canada', id: 'ca' }, + { label: 'Pakistan', value: 'Pakistan', id: 'pk' }, + ] + +%> + +<%= pb_rails("dropdown", props: {options: options, default_value: options.last}) %> diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_value.jsx b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_value.jsx new file mode 100644 index 0000000000..303635e11c --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_value.jsx @@ -0,0 +1,31 @@ +import React from 'react' +import { Dropdown } from '../../' + +const DropdownDefaultValue = (props) => { + const options = [ + { + label: "United States", + value: "United States", + }, + { + label: "Canada", + value: "Canada", + }, + { + label: "Pakistan", + value: "Pakistan", + } + ]; + + return ( + <> + <Dropdown + defaultValue={options[2]} + options={options} + {...props} + /> + </> + ) +} + +export default DropdownDefaultValue diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/example.yml b/playbook/app/pb_kits/playbook/pb_dropdown/docs/example.yml index b2a2557c95..5f4d445870 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/example.yml @@ -8,6 +8,8 @@ examples: - dropdown_with_custom_trigger_rails: Custom Trigger - dropdown_with_custom_padding: Custom Option Padding - dropdown_error: Dropdown with Error + - dropdown_default_value: Default Value + - dropdown_blank_selection: Blank Selection react: - dropdown_default: Default @@ -18,6 +20,8 @@ examples: - dropdown_with_custom_trigger: Custom Trigger - dropdown_with_custom_padding: Custom Option Padding - dropdown_error: Dropdown with Error + - dropdown_default_value: Default Value + - dropdown_blank_selection: Blank Selection # - dropdown_with_autocomplete: Autocomplete # - dropdown_with_autocomplete_and_custom_display: Autocomplete with Custom Display # - dropdown_with_external_control: useDropdown Hook diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/index.js b/playbook/app/pb_kits/playbook/pb_dropdown/docs/index.js index 637c6f517f..92feffe44a 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/index.js @@ -9,4 +9,6 @@ export { default as DropdownWithLabel } from './_dropdown_with_label.jsx' export { default as DropdownWithExternalControl } from './_dropdown_with_external_control.jsx' export { default as DropdownWithHook } from './_dropdown_with_hook.jsx' export { default as DropdownSubcomponentStructure } from './_dropdown_subcomponent_structure.jsx' -export { default as DropdownError } from './_dropdown_error.jsx' \ No newline at end of file +export { default as DropdownError } from './_dropdown_error.jsx' +export { default as DropdownDefaultValue } from './_dropdown_default_value.jsx' +export { default as DropdownBlankSelection } from './_dropdown_blank_selection.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb b/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb index a233b70c76..fc2a261045 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +++ b/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb @@ -8,8 +8,8 @@ <%= pb_rails("caption", props: {text: object.label, margin_bottom:"xs"}) %> <% end %> <div class="dropdown_wrapper<%= error_class %>" style="position: relative"> - <input type="hidden" name="<%= object.name %>" id="dropdown-selected-option" value="" /> - <input id="dropdown-form-validation" name="<%= object.name %>_form_validation" value="" style="display: none" <%= object.required ? "required" : ""%> /> + <input type="hidden" name="<%= object.name %>" id="dropdown-selected-option" value="<%= input_default_value %>" /> + <input id="dropdown-form-validation" name="<%= object.name %>_form_validation" value="<%= input_default_value %>" style="display: none" <%= object.required ? "required" : ""%> /> <% if content.present? %> <%= content.presence %> @@ -17,8 +17,8 @@ <% else %> <%= pb_rails("dropdown/dropdown_trigger") %> <%= pb_rails("dropdown/dropdown_container") do %> - <% if object.options.present? %> - <% object.options.each do |option| %> + <% if options_with_blank.present? %> + <% options_with_blank.each do |option| %> <%= pb_rails("dropdown/dropdown_option", props: {option: option}) %> <% end %> <% end %> diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.rb b/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.rb index 3720b1b9d5..d390b7e015 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.rb +++ b/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.rb @@ -10,6 +10,9 @@ class Dropdown < Playbook::KitBase prop :error, type: Playbook::Props::String prop :required, type: Playbook::Props::Boolean, default: false + prop :default_value + prop :blank_selection, type: Playbook::Props::String, + default: "" def data Hash(prop(:data)).merge(pb_dropdown: true) @@ -24,6 +27,14 @@ def classname def error_class error.present? ? " error" : "" end + + def input_default_value + default_value.present? ? default_value.transform_keys(&:to_s) : "" + end + + def options_with_blank + blank_selection.present? ? [{ id: "", value: "", label: blank_selection }] + options : options + end end end end diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb b/playbook/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb index 84fb4a2c8f..e34e562dbf 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb +++ b/playbook/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb @@ -11,7 +11,7 @@ class DropdownTrigger < Playbook::KitBase prop :custom_display def data - Hash(prop(:data)).merge(dropdown_trigger: true) + Hash(prop(:data)).merge(dropdown_trigger: true, dropdown_placeholder: default_display_placeholder) end def classname diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/index.js b/playbook/app/pb_kits/playbook/pb_dropdown/index.js index a1e833822f..42219cda92 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/index.js +++ b/playbook/app/pb_kits/playbook/pb_dropdown/index.js @@ -9,6 +9,8 @@ const UP_ARROW_SELECTOR = "#dropdown_close_icon"; const OPTION_SELECTOR = "[data-dropdown-option-label]"; const CUSTOM_DISPLAY_SELECTOR = "[data-dropdown-custom-trigger]"; const INPUT_FORM_VALIDATION = "#dropdown-form-validation"; +const DROPDOWN_TRIGGER_DISPLAY = "#dropdown_trigger_display"; +const DROPDOWN_PLACEHOLDER = "[data-dropdown-placeholder]"; export default class PbDropdown extends PbEnhancedElement { static get selector() { @@ -21,9 +23,11 @@ export default class PbDropdown extends PbEnhancedElement { connect() { this.keyboardHandler = new PbDropdownKeyboard(this); + this.setDefaultValue(); this.bindEventListeners(); this.updateArrowDisplay(false); this.handleFormValidation(); + this.handleFormReset(); } bindEventListeners() { @@ -83,9 +87,7 @@ export default class PbDropdown extends PbEnhancedElement { } onOptionSelected(value, selectedOption) { - const triggerElement = this.element.querySelector( - "#dropdown_trigger_display" - ); + const triggerElement = this.element.querySelector(DROPDOWN_TRIGGER_DISPLAY); const customDisplayElement = this.element.querySelector( "#dropdown_trigger_custom_display" ); @@ -179,4 +181,53 @@ export default class PbDropdown extends PbEnhancedElement { } } } + + setDefaultValue() { + const hiddenInput = this.element.querySelector("#dropdown-selected-option"); + + if (hiddenInput.value) { + const inputFormValidation = this.element.querySelector(INPUT_FORM_VALIDATION); + const defaultValue = JSON.parse(hiddenInput.value.replaceAll("=>", ":")); + const options = this.element.querySelectorAll(OPTION_SELECTOR); + + options.forEach((option) => { + if (defaultValue.id === JSON.parse(option.dataset.dropdownOptionLabel).id) { + option.classList.add("pb_dropdown_option_selected"); + } + }); + + this.setTriggerElementText(defaultValue.label); + + hiddenInput.value = defaultValue.id; + inputFormValidation.value = defaultValue.id; + } + } + + handleFormReset() { + const form = this.element.closest("form"); + + if (form) { + form.addEventListener("reset", () => { + this.resetDropdownValue(); + }); + } + } + + resetDropdownValue() { + const hiddenInput = this.element.querySelector("#dropdown-selected-option"); + const inputFormValidation = this.element.querySelector(INPUT_FORM_VALIDATION); + + hiddenInput.value = ""; + inputFormValidation.value = ""; + + const defaultPlaceholder = this.element.querySelector(DROPDOWN_PLACEHOLDER); + this.setTriggerElementText(defaultPlaceholder.dataset.dropdownPlaceholder); + } + + setTriggerElementText(text) { + const triggerElement = this.element.querySelector(DROPDOWN_TRIGGER_DISPLAY); + if (triggerElement) { + triggerElement.textContent = text; + } + } } diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.scss b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.scss index 2fc706d21f..f2a179f3e9 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +++ b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.scss @@ -8,6 +8,7 @@ $selector: ".pb_form_pill"; $pb_form_pill_height: 37px; $form_pill_colors: ( primary: map-get($status_color_text, "primary"), + neutral: map-get($status_color_text, "neutral"), ); @@ -23,34 +24,71 @@ $form_pill_colors: ( cursor: pointer; @each $color_name, $color_value in $form_pill_colors { &[class*=_#{$color_name}] { - background-color: rgba($color_value, $opacity-1); + background-color: mix($color_value, $card_light, 10%); + @if ($color_name == "neutral") { + background-color: $white; + border: 1px solid $border_light; + } transition: background-color 0.2s ease; box-shadow: none; @media (hover:hover) { &:hover { - background-color: rgba($color_value, $opacity-2); + background-color: mix($color_value, $card_light, 20%); + @if ($color_name == "neutral") { + background-color: mix($neutral, $card_light, 20%); + border: 1px solid $border_light; + } + } + &:active { + background-color: mix($color_value, $card_light, 30%); + @if ($color_name == "neutral") { + background-color: mix($neutral, $card_light, 30%); + } } } #{$selector}_text { color: $color_value; + @if ($color_name == "neutral") { + color: $text_lt_default; + } padding-left: $space-sm; - padding-right: $space-sm/4; + padding-right: $space-sm/2; } #{$selector}_close { color: $color_value; - padding-left: $space-sm/2; + padding-left: $space-sm/4; padding-right: $space-sm/4; display: flex; align-items: center; - height: 100%; + // I had to temporarily change height to 27px so new hover state darker background forms a circle not an oval + // before size change (ticket 2 of 4) - change back to 100% when $pb_form_pill_height changed to 27px from 37px + height: 27px; + border-radius: 70px; cursor: pointer; + &:hover { + background-color: mix($color_value, $card_light, 40%); + @if ($color_name == "neutral") { + background-color: mix($neutral, $card_light, 60%); + } + } } #{$selector}_tag { color: $color_value; padding-left: $space-sm; + @if ($color_name == "neutral") { + color: $text_lt_default; + } } } } + &:focus { + outline: $primary solid 2px; + outline-offset: -1px; + } + &:focus-visible { + outline: $primary solid 2px; + outline-offset: -1px; + } &.small { height: fit-content; height: -moz-fit-content; @@ -70,6 +108,71 @@ $form_pill_colors: ( &::before { line-height: 21px; } } } + &.dark { + @each $color_name, $color_value in $form_pill_colors { + // In dark mode, the base patterns below are needed for the next tickets for success, warning, error, info. + // Primary and Neutral are exceptions to the general rule in the handoff + &[class*=_#{$color_name}] { + // background-color: mix($color_value, $card_dark, 10%); + // .pb_form_pill_tag { + // color: $color_name; + // } + // .pb_form_pill_close { + // color: $color_name; + // &:hover { + // background-color: mix($color_value, $card_dark, 40%); + // } + // } + // &:hover { + // background-color: mix($color_value, $card_dark, 20%); + // } + // &:active { + // background-color: mix($color_value, $card_dark, 30%); + // } + @if ($color_name == "neutral") { + background-color: transparent; + border: 1px solid $border_dark; + .pb_form_pill_text, .pb_form_pill_tag { + color: $text_dk_default; + } + .pb_form_pill_close { + color: $text_dk_default; + &:hover { + background-color: mix($neutral, $card_dark, 40%); + } + } + &:hover { + background-color: mix($white, $card_dark, 10%); + } + &:active { + background-color: mix($white, $card_dark, 20%); + } + &:focus { + border: 1px solid $primary; + } + } + @if ($color_name == "primary") { + background-color: mix($active_dark, $card_dark, 10%); + .pb_form_pill_text, .pb_form_pill_tag { + color: $active_dark; + } + .pb_form_pill_close { + color: $active_dark; + &:hover { + background-color: mix($active_dark, $card_dark, 40%); + } + } + &:hover { + background-color: mix($active_dark, $card_dark, 20px); + } + &:active { + background-color: mix($active_dark, $card_dark, 30%); + } + } + } + } + } + &[class*=lowercase] { text-transform: lowercase; } diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.test.jsx b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.test.jsx new file mode 100644 index 0000000000..7c3d2e9331 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.test.jsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { render, screen } from '../utilities/test-utils'; +import FormPill from './_form_pill'; + +const testId = 'formpill'; + +test('should render classname', () => { + render( + <FormPill + data={{ testid: testId }} + text="test" + /> + ) + + const kit = screen.getByTestId(testId) + expect(kit).toHaveClass('pb_form_pill_kit_primary none') +}); + +test('displays text content', () => { + render( + <FormPill + data={{ testid: testId }} + text="test" + /> + ) + + const text = screen.getByText("test") + expect(text).toBeInTheDocument() +}); + +test('displays color variant', () => { + render( + <FormPill + color={"neutral"} + data={{ testid: testId }} + text={"test"} + /> + ) + const kit = screen.getByTestId(testId) + expect(kit).toHaveClass(`pb_form_pill_kit_neutral none`) +}); + +test('displays size variant', () => { + render( + <FormPill + data={{ testid: testId }} + size={"small"} + text={"test"} + /> + ) + const kit = screen.getByTestId('formpill') + expect(kit).toHaveClass(`pb_form_pill_kit_primary small none`) +}); \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx index 107a122abe..92f391edcf 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +++ b/playbook/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx @@ -5,7 +5,7 @@ import Title from '../pb_title/_title' import Icon from '../pb_icon/_icon' import Avatar from '../pb_avatar/_avatar' import { globalProps, GlobalProps } from '../utilities/globalProps' -import { buildHtmlProps } from '../utilities/props' +import { buildDataProps, buildHtmlProps } from '../utilities/props' type FormPillProps = { className?: string, @@ -18,6 +18,9 @@ type FormPillProps = { avatarUrl?: string, size?: string, textTransform?: 'none' | 'lowercase', + color?: "primary" | "neutral", + data?: {[key: string]: string}, + tabIndex?: number, closeProps?: { onClick?: React.MouseEventHandler<HTMLSpanElement>, onMouseDown?: React.MouseEventHandler<HTMLSpanElement>, @@ -36,20 +39,26 @@ const FormPill = (props: FormPillProps): React.ReactElement => { closeProps = {}, size = '', textTransform = 'none', + color = "primary", + data = {}, + tabIndex, } = props const css = classnames( - `pb_form_pill_kit_${'primary'}`, + `pb_form_pill_kit_${color}`, globalProps(props), className, size === 'small' ? 'small' : null, textTransform, ) + const dataProps = buildDataProps(data) const htmlProps = buildHtmlProps(htmlOptions) return ( <div className={css} id={id} + tabIndex={tabIndex} + {...dataProps} {...htmlProps} > {name && diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_example.html.erb b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_example.html.erb index 9685a0e76a..ea2ee33b81 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_example.html.erb +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_example.html.erb @@ -1 +1,5 @@ -<%= pb_rails("form_pill", props: { text_transform: "lowercase" , text: "THIS IS A TAG" }) %> \ No newline at end of file +<%= pb_rails("form_pill", props: { + text_transform: "lowercase" , + text: "THIS IS A TAG", + tabindex: 0, +}) %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_example.jsx b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_example.jsx index 8b4e86a938..6f1f72545b 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_example.jsx +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_example.jsx @@ -6,6 +6,7 @@ const FormPillExample = (props) => { <div> <FormPill onClick={() => alert('Click!')} + tabIndex={0} text="THIS IS A TAG" textTransform="lowercase" {...props} diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.html.erb b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.html.erb index e20377d996..5944aa0b7e 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.html.erb +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.html.erb @@ -2,6 +2,7 @@ name: "Anna Black", avatar_url: "https://randomuser.me/api/portraits/women/44.jpg", size: "small", + tabindex: 0, }) %> <br /> @@ -10,4 +11,5 @@ <%= pb_rails("form_pill", props: { name: "Anna Black", size: "small", + tabindex: 0, }) %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.jsx b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.jsx index 5bb3b96d18..696c8a619f 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.jsx +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.jsx @@ -9,6 +9,7 @@ const FormPillSize = (props) => { avatarUrl="https://randomuser.me/api/portraits/women/44.jpg" name="Anna Black" size="small" + tabIndex={0} {...props} /> <br /> @@ -16,6 +17,7 @@ const FormPillSize = (props) => { <FormPill name="Anna Black" size="small" + tabIndex={0} {...props} /> </div> diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_tag.html.erb b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_tag.html.erb index 631c505ba9..c2cb038771 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_tag.html.erb +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_tag.html.erb @@ -1 +1,4 @@ -<%= pb_rails("form_pill", props: { text: "this is a tag" }) %> \ No newline at end of file +<%= pb_rails("form_pill", props: { + text: "this is a tag", + tabindex: 0, +}) %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_tag.jsx b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_tag.jsx index fca0242613..3abf85a6ac 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_tag.jsx +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_tag.jsx @@ -6,8 +6,9 @@ const FormPillDefault = (props) => { <div> <FormPill onClick={() => { -alert('Click!') -}} + alert('Click!') + }} + tabIndex={0} text="this is a tag" {...props} /> diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.html.erb b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.html.erb index b0621965a3..5473345ca0 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.html.erb +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.html.erb @@ -1,6 +1,7 @@ <%= pb_rails("form_pill", props: { name: "Anna Black", avatar_url: "https://randomuser.me/api/portraits/women/44.jpg", + tabindex: 0, }) %> <br /> @@ -8,4 +9,5 @@ <%= pb_rails("form_pill", props: { name: "Anna Black", + tabindex: 0, }) %> diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.jsx b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.jsx index f72771da18..626a44eb5c 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.jsx +++ b/playbook/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.jsx @@ -9,6 +9,7 @@ const FormPillDefault = (props) => { avatarUrl="https://randomuser.me/api/portraits/women/44.jpg" name="Anna Black" onClick={() => alert('Click!')} + tabIndex={0} {...props} /> <br /> @@ -16,6 +17,7 @@ const FormPillDefault = (props) => { <FormPill name="Anna Black" onClick={() => alert('Click!')} + tabIndex={0} {...props} /> </div> diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb b/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb index 71e74b5c4f..bb160b8b14 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb +++ b/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb @@ -1,4 +1,4 @@ -<%= content_tag(:div, id: object.id, data: object.data, class: object.classname + object.size_class, **combined_html_options) do %> +<%= content_tag(:div, id: object.id, data: object.data, class: object.classname + object.size_class, tabindex: object.tabindex, **combined_html_options) do %> <% if object.name.present? %> <%= pb_rails("avatar", props: { name: object.name, image_url: object.avatar_url, size: "xs" }) %> <%= pb_rails("title", props: { text: object.name, size: 4, classname: "pb_form_pill_text" }) %> diff --git a/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.rb b/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.rb index 8a098856f1..402a516750 100644 --- a/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.rb +++ b/playbook/app/pb_kits/playbook/pb_form_pill/form_pill.rb @@ -11,9 +11,13 @@ class FormPill < Playbook::KitBase prop :text_transform, type: Playbook::Props::Enum, values: %w[none lowercase], default: "none" + prop :color, type: Playbook::Props::Enum, + values: %w[primary neutral], + default: "primary" + prop :tabindex def classname - generate_classname("pb_form_pill_kit", "primary", name, text, text_transform) + generate_classname("pb_form_pill_kit", color, name, text, text_transform) end def display_text diff --git a/playbook/app/pb_kits/playbook/pb_icon/_icon.scss b/playbook/app/pb_kits/playbook/pb_icon/_icon.scss index 605e1c3777..59aa945237 100644 --- a/playbook/app/pb_kits/playbook/pb_icon/_icon.scss +++ b/playbook/app/pb_kits/playbook/pb_icon/_icon.scss @@ -1,4 +1,3 @@ -// Rails custom icon styles svg.pb_custom_icon { width: 1em; fill: currentColor; @@ -10,3 +9,213 @@ svg.pb_custom_icon { .pb_icon_kit_emoji { font-family: monospace; } + +$rotate-list: (90, 180, 270); + +@keyframes pb_icon_spin { + 0% { + -webkit-transform: rotate(0); + transform: rotate(0); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +}; + +svg { + &.pb_icon_kit, + &.pb_custom_icon{ + @each $r in $rotate-list { + &.rotate_#{$r} { + transform: rotate(#{$r}deg); + } + } + &.flip_horizontal { + transform: scaleX(-1); + } + &.flip_vertical { + transform: scaleY(-1); + } + &.flip_horizontal.flip_vertical { + transform: scaleX(-1) scaleY(-1); + } + &.svg-inline--fa { + height: 1em; + overflow: visible; + vertical-align: -.125em + } + &.svg_inverse { + color: #fff; + } + &.svg_border { + border-color: #eee; + border-radius: .1em; + border-style: solid; + border-width: .08em; + padding: .2em .25em .15em; + } + &.svg_fw { + text-align: center; + width: 1.25em + } + &.svg_li { + left: calc(2em * -1); + position: absolute; + text-align: center; + width: 2em; + line-height: inherit + } + &.pull_left { + float: left; + margin-right: .3em; + } + + &.pull_right { + float: right; + margin-left: .3em; + } + &.pulse { + animation-name: pb_icon_spin; + animation-direction: normal; + animation-duration: 1s; + animation-iteration-count: infinite; + animation-timing-function: steps(8); + } + &.spin { + animation-name: pb_icon_spin; + animation-delay: 0s; + animation-direction: normal; + animation-duration: 2s; + animation-iteration-count: infinite; + animation-timing-function: linear; + } + + &.svg_xs { + font-size: 0.75em + } + + &.svg_sm { + font-size: 0.875em + } + + &.svg_lg { + font-size: 1.25em + } + + &.svg_1x { + font-size: 1em + } + + &.svg_2x { + font-size: 2em + } + + &.svg_3x { + font-size: 3em + } + + &.svg_4x { + font-size: 4em + } + + &.svg_5x { + font-size: 5em + } + + &.svg_6x { + font-size: 6em + } + + &.svg_7x { + font-size: 7em + } + + &.svg_8x { + font-size: 8em + } + + &.svg_9x { + font-size: 9em + } + + &.svg_10x { + font-size: 10em + } + &.fa-xs { + font-size: .75em; + line-height: .0833333337em; + vertical-align: .125em + } + &.fa-sm { + font-size: .875em; + line-height: .0714285718em; + vertical-align: .0535714295em + } + &.fa-lg { + font-size: 1.25em; + line-height: .05em; + vertical-align: -.075em + } + &.fa-pull-left { + float: left; + margin-right: .3em; + } + + &.fa-pull-right { + float: right; + margin-left: .3em; + } + &.fa-li { + left: calc(2em * -1); + position: absolute; + text-align: center; + width: 2em; + line-height: inherit + } + &.svg-inline--fa.fa-li { + width: 2em; + top: .25em + } + &.svg-inline--fa.fa-fw { + width: 1.25em; + } + &.fa-fw { + text-align: center; + width: 1.25em + } + &.fa-layers { + display: inline-block; + height: 1em; + position: relative; + text-align: center; + vertical-align: -.125em; + width: 1em + } + &.fa-2x { + font-size: 2em + } + &.fa-3x { + font-size: 3em + } + &.fa-flip { + animation-name: fa-flip; + animation-delay: 0s; + animation-direction: normal; + animation-duration: 1s; + animation-iteration-count: infinite; + animation-timing-function: ease-in-out; + } + &.fa-spin { + animation-name: fa-spin; + animation-delay: 0s; + animation-direction: normal; + animation-duration: 2s; + animation-iteration-count: infinite; + animation-timing-function: linear; + } + &.fa-pulse { + animation: fa-spin 1s infinite linear; + } + } +} diff --git a/playbook/app/pb_kits/playbook/pb_icon/_icon.tsx b/playbook/app/pb_kits/playbook/pb_icon/_icon.tsx index 3f56d15701..c7ccbb9d43 100644 --- a/playbook/app/pb_kits/playbook/pb_icon/_icon.tsx +++ b/playbook/app/pb_kits/playbook/pb_icon/_icon.tsx @@ -3,7 +3,6 @@ import classnames from 'classnames' import { buildAriaProps, buildDataProps, buildHtmlProps } from '../utilities/props' import { GlobalProps, globalProps } from '../utilities/globalProps' import { isValidEmoji } from '../utilities/validEmojiChecker' -import aliasesJson from './icon_aliases.json' export type IconSizes = "lg" | "xs" @@ -41,24 +40,75 @@ type IconProps = { spin?: boolean, } & GlobalProps -type AliasType = string | string[]; - -interface Aliases { - [key: string]: AliasType; +const flipMap = { + fa: { + horizontal: 'fa-flip-horizontal', + vertical: 'fa-flip-vertical', + both: 'fa-flip-horizontal fa-flip-vertical', + none: '' + }, + svg: { + horizontal: 'flip_horizontal', + vertical: 'flip_vertical', + both: 'flip_horizontal flip_vertical', + none: '' + } } - -interface AliasesJson { - aliases: Aliases; +const pulseMap = { + fa: 'fa-pulse', + svg: 'pulse' +} +const spinMap = { + fa: 'fa-spin', + svg: 'spin' +} +const rotateMap = { + fa: { + 90: 'fa-rotate-90', + 180: 'fa-rotate-180', + 270: 'fa-rotate-270' + }, + svg: { + 90: 'rotate_90', + 180: 'rotate_180', + 270: 'rotate_270' + } } -const aliases: AliasesJson = aliasesJson; - +const sizeMap = { + fa: { + "lg": "fa-lg", + "xs": "fa-xs", + "sm": "fa-sm", + "1x": "fa-1x", + "2x": "fa-2x", + "3x": "fa-3x", + "4x": "fa-4x", + "5x": "fa-5x", + "6x": "fa-6x", + "7x": "fa-7x", + "8x": "fa-8x", + "9x": "fa-9x", + "10x": "fa-10x", + "": "" + }, + svg: { + "lg": "svg_lg", + "xs": "svg_xs", + "sm": "svg_sm", + "1x": "svg_1x", + "2x": "svg_2x", + "3x": "svg_3x", + "4x": "svg_4x", + "5x": "svg_5x", + "6x": "svg_6x", + "7x": "svg_7x", + "8x": "svg_8x", + "9x": "svg_9x", + "10x": "svg_10x", + "": "" + } -const flipMap = { - horizontal: 'fa-flip-horizontal', - vertical: 'fa-flip-vertical', - both: 'fa-flip-horizontal fa-flip-vertical', - none: "" } declare global { @@ -66,22 +116,6 @@ declare global { var PB_ICONS: {[key: string]: React.FunctionComponent<any>} } -// Resolve alias function -const resolveAlias = (icon: string): string => { - const alias = aliases.aliases[icon]; - - if (alias) { - if (Array.isArray(alias)) { - return alias[0]; - } else { - return alias; - } - } - - return icon; -}; - - const Icon = (props: IconProps) => { const { aria = {}, @@ -104,8 +138,7 @@ const Icon = (props: IconProps) => { spin = false, } = props - const resolvedIcon = resolveAlias(icon as string) - let iconElement: ReactSVGElement | null = typeof(resolvedIcon) === "object" ? resolvedIcon : null + let iconElement: ReactSVGElement | null = typeof(icon) === "object" ? icon : null const faClasses = { 'fa-border': border, @@ -121,32 +154,58 @@ const Icon = (props: IconProps) => { if (!customIcon && !iconElement) { const PowerIcon: React.FunctionComponent<any> | undefined = - window.PB_ICONS ? window.PB_ICONS[resolvedIcon as string] : null + window.PB_ICONS ? window.PB_ICONS[icon as string] : null if (PowerIcon) { iconElement = <PowerIcon /> as ReactSVGElement } else { - faClasses[`fa-${resolvedIcon}`] = resolvedIcon as string + faClasses[`fa-${icon}`] = icon as string } } - const classes = classnames( - flipMap[flip], + const isFA = !iconElement && !customIcon + + let classes = classnames( (!iconElement && !customIcon) ? 'pb_icon_kit' : '', (iconElement || customIcon) ? 'pb_custom_icon' : fontStyle, iconElement ? 'svg-inline--fa' : '', - faClasses, globalProps(props), className ) + const transformClasses = classnames( + flip ? flipMap[isFA ? 'fa' : 'svg'][flip] : null, + pulse ? pulseMap[isFA ? 'fa' : 'svg'] : null, + rotation ? rotateMap[isFA ? 'fa' : 'svg'][rotation] : null, + spin ? spinMap[isFA ? 'fa' : 'svg'] : null, + size ? sizeMap[isFA ? 'fa' : 'svg'][size] : null, + border ? isFA ? 'fa-border' : 'svg_border' : null, + fixedWidth ? isFA ? 'fa-fw' : 'svg_fw' : null, + inverse ? isFA ? 'fa-inverse' : 'svg_inverse' : null, + listItem ? isFA ? 'fa-li' : 'svg_li' : null, + pull ? isFA ? `fa-pull-${pull}` : `pull_${pull}` : null, + ) + classes += ` ${transformClasses}` + + if (isFA) { + const faClassList = { + 'fa-border': border, + 'fa-inverse': inverse, + 'fa-li': listItem, + [`fa-${size}`]: size, + [`fa-pull-${pull}`]: pull, + } + faClassList[`fa-${icon}`] = icon as string + classes += ` ${classnames(faClassList)}` + } + const classesEmoji = classnames( 'pb_icon_kit_emoji', globalProps(props), className ) - aria.label ? null : aria.label = `${resolvedIcon} icon` + aria.label ? null : aria.label = `${icon} icon` const ariaProps: {[key: string]: any} = buildAriaProps(aria) const dataProps: {[key: string]: any} = buildDataProps(data) const htmlProps = buildHtmlProps(htmlOptions) @@ -168,7 +227,7 @@ const Icon = (props: IconProps) => { } </> ) - else if (isValidEmoji(resolvedIcon as string)) + else if (isValidEmoji(icon as string)) return ( <> <span @@ -177,7 +236,7 @@ const Icon = (props: IconProps) => { className={classesEmoji} id={id} > - {resolvedIcon} + {icon} </span> </> ) diff --git a/playbook/app/pb_kits/playbook/pb_icon/icon.rb b/playbook/app/pb_kits/playbook/pb_icon/icon.rb index 4b688733ee..eb9ba3c05a 100644 --- a/playbook/app/pb_kits/playbook/pb_icon/icon.rb +++ b/playbook/app/pb_kits/playbook/pb_icon/icon.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -# rubocop:disable Style/HashLikeCase - require "open-uri" require "json" @@ -39,8 +37,6 @@ class Icon < Playbook::KitBase prop :spin, type: Playbook::Props::Boolean, default: false - ALIASES = JSON.parse(File.read(Playbook::Engine.root.join("app/pb_kits/playbook/pb_icon/icon_aliases.json")))["aliases"].freeze - def valid_emoji? emoji_regex = /\p{Emoji}/ emoji_regex.match?(icon) @@ -82,6 +78,14 @@ def custom_icon_classname ) end + def icon_alias_map + return unless Rails.application.config.respond_to?(:icon_alias_path) + + base_path = Rails.application.config.icon_alias_path + json = File.read(Rails.root.join(base_path)) + JSON.parse(json)["aliases"].freeze + end + def asset_path return unless Rails.application.config.respond_to?(:icon_path) @@ -111,7 +115,9 @@ def is_svg? private def resolve_alias(icon) - aliases = ALIASES[icon] + return icon unless icon_alias_map + + aliases = icon_alias_map[icon] return icon unless aliases if aliases.is_a?(Array) @@ -131,11 +137,13 @@ def svg_size end def border_class - border ? "fa-border" : nil + prefix = is_svg? ? "svg_border" : "fa-border" + border ? prefix : nil end def fixed_width_class - fixed_width ? "fa-fw" : nil + prefix = is_svg? ? "svg_fw" : "fa-fw" + fixed_width ? prefix : nil end def icon_class @@ -143,38 +151,45 @@ def icon_class end def inverse_class - inverse ? "fa-inverse" : nil + class_name = is_svg? ? "svg_inverse" : "fa-inverse" + inverse ? class_name : nil end def list_item_class - list_item ? "fa-li" : nil + class_name = is_svg? ? "svg_li" : "fa-li" + list_item ? class_name : nil end def flip_class + prefix = is_svg? ? "flip_" : "fa-flip-" case flip when "horizontal" - "fa-flip-horizontal" + "#{prefix}horizontal" when "vertical" - "fa-flip-vertical" + "#{prefix}vertical" when "both" - "fa-flip-horizontal fa-flip-vertical" + "#{prefix}horizontal #{prefix}vertical" end end def pull_class - pull ? "fa-pull-#{pull}" : nil + class_name = is_svg? ? "pull_#{pull}" : "fa-pull-#{pull}" + pull ? class_name : nil end def pulse_class - pulse ? "fa-pulse" : nil + class_name = is_svg? ? "pulse" : "fa-pulse" + pulse ? class_name : nil end def rotation_class - rotation ? "fa-rotate-#{rotation}" : nil + class_name = is_svg? ? "rotate_#{rotation}" : "fa-rotate-#{rotation}" + rotation ? class_name : nil end def size_class - size ? "fa-#{size}" : nil + class_name = is_svg? ? "svg_#{size}" : "fa-#{size}" + size ? class_name : nil end def font_style_class @@ -182,10 +197,9 @@ def font_style_class end def spin_class - spin ? "fa-spin" : nil + class_name = is_svg? ? "spin" : "fa-spin" + spin ? class_name : nil end end end end - -# rubocop:enable Style/HashLikeCase diff --git a/playbook/app/pb_kits/playbook/pb_icon/icon_aliases.json b/playbook/app/pb_kits/playbook/pb_icon/icon_aliases.json deleted file mode 100644 index 411c10f0d7..0000000000 --- a/playbook/app/pb_kits/playbook/pb_icon/icon_aliases.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "aliases": { - "arrow-alt-circle-right": "circle-right", - "angles-down": "angle-double-down", - "arrow-alt-down": "down", - "arrow-alt-up": "up", - "arrow-right-long": "long-arrow-right", - "arrow-to-bottom": "arrow-down-to-line", - "arrows-h": "arrows-left-right", - "calendar-days": "calendar-alt", - "circle-arrow-right": "arrow-circle-right", - "clock-rotate-left": "history", - "close": [ - "times", - "xmark" - ], - "ellipsis-h": "ellipsis", - "exclamation-circle": "circle-exclamation", - "external-link": "arrow-up-right-from-square", - "file-lines": "file-alt", - "gear": "cog", - "home": "house", - "info-circle": "circle-info", - "map-o": "map", - "message": "comment-alt", - "minus-circle": "circle-minus", - "money": "money-bill", - "mouse-pointer": "arrow-pointer", - "nitro": "nitro-n", - "play-circle": "circle-play", - "plus-circle": "circle-plus", - "plus-square": "square-plus", - "powergon": "powergon-p", - "question-circle": "circle-question", - "roofing": "product-roofing", - "shelves": "inventory", - "th-list": "table-list" - } - } \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_nav/_nav_item.test.js b/playbook/app/pb_kits/playbook/pb_nav/_nav_item.test.js index d9e8939748..25256a6e0e 100644 --- a/playbook/app/pb_kits/playbook/pb_nav/_nav_item.test.js +++ b/playbook/app/pb_kits/playbook/pb_nav/_nav_item.test.js @@ -95,11 +95,11 @@ test('should not have a left border', () => { test('should have a right icon', () => { render(<NavDefault iconRight="angle-down" />) const kit = screen.getByTestId(itemTestId) - expect(kit).toContainHTML('<i class="pb_icon_kit far fa-fw fa-angle-down pb_nav_list_item_icon_right" />') + expect(kit).toContainHTML('<i class="pb_icon_kit far pb_nav_list_item_icon_right fa-fw fa-angle-down" />') }) test('should have a left icon', () => { render(<NavDefault iconLeft="users-class" />) const kit = screen.getByTestId(itemTestId) - expect(kit).toContainHTML('<i class="pb_icon_kit far fa-fw fa-users-class pb_nav_list_item_icon_left" />') + expect(kit).toContainHTML('<i class="pb_icon_kit far pb_nav_list_item_icon_left fa-fw fa-users-class" />') }) diff --git a/playbook/app/pb_kits/playbook/pb_rich_text_editor/TipTap/MoreExtensionsDropdown.tsx b/playbook/app/pb_kits/playbook/pb_rich_text_editor/TipTap/MoreExtensionsDropdown.tsx index 07af2c4d0c..26e5b68d30 100644 --- a/playbook/app/pb_kits/playbook/pb_rich_text_editor/TipTap/MoreExtensionsDropdown.tsx +++ b/playbook/app/pb_kits/playbook/pb_rich_text_editor/TipTap/MoreExtensionsDropdown.tsx @@ -10,7 +10,7 @@ const MoreExtensionsDropdown = ({extensions}: any) => { const [showPopover, setShowPopover] = useState(false) const handleTogglePopover = () => { - setShowPopover(true) + setShowPopover(!showPopover) } const handlePopoverClose = (shouldClosePopover: boolean) => { diff --git a/playbook/app/pb_kits/playbook/pb_rich_text_editor/TipTap/ToolbarDropdown.tsx b/playbook/app/pb_kits/playbook/pb_rich_text_editor/TipTap/ToolbarDropdown.tsx index a355a98ffa..562c1c04e9 100644 --- a/playbook/app/pb_kits/playbook/pb_rich_text_editor/TipTap/ToolbarDropdown.tsx +++ b/playbook/app/pb_kits/playbook/pb_rich_text_editor/TipTap/ToolbarDropdown.tsx @@ -67,7 +67,7 @@ const toolbarDropdownItems = [ const handleTogglePopover = () => { - setShowPopover(true) + setShowPopover(!showPopover) } const handlePopoverClose = (shouldClosePopover: boolean) => { diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/_star_rating.scss b/playbook/app/pb_kits/playbook/pb_star_rating/_star_rating.scss index f40cd68086..0965960c72 100644 --- a/playbook/app/pb_kits/playbook/pb_star_rating/_star_rating.scss +++ b/playbook/app/pb_kits/playbook/pb_star_rating/_star_rating.scss @@ -48,8 +48,8 @@ $star-styles: ( - yellow_star: (color: #F9BB00), - primary_star: (color: #0056CF), + yellow_star: (color: $yellow), + primary_star: (color: $royal), suble_star_light: (color: $text_lt_default), suble_star_dark: (color: $text_dk_default), empty_star_dark: (color: $border_dark), @@ -111,4 +111,13 @@ } } } + .yellow-star-selected { + color: $yellow; + } + .primary-star-selected { + color: $royal + } + .suble-star-selected { + color: $text_lt_default; + } } diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/docs/_star_rating_interactive.html.erb b/playbook/app/pb_kits/playbook/pb_star_rating/docs/_star_rating_interactive.html.erb new file mode 100644 index 0000000000..41b1f249b1 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_star_rating/docs/_star_rating_interactive.html.erb @@ -0,0 +1 @@ +<%= pb_rails("star_rating", props: { padding_bottom: "xs", variant: "interactive" }) %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/docs/example.yml b/playbook/app/pb_kits/playbook/pb_star_rating/docs/example.yml index d367d24b39..9253d7b9c7 100644 --- a/playbook/app/pb_kits/playbook/pb_star_rating/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_star_rating/docs/example.yml @@ -13,4 +13,4 @@ examples: - star_rating_background_options: Background Options - star_rating_hide: Layout Options - star_rating_number_config: Number Config - - star_rating_size_options: Size Options + - star_rating_size_options: Size Options \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/index.js b/playbook/app/pb_kits/playbook/pb_star_rating/index.js new file mode 100644 index 0000000000..34b8eb3859 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_star_rating/index.js @@ -0,0 +1,50 @@ +import PbEnhancedElement from "../pb_enhanced_element"; + +const STAR_RATING_SELECTOR = "[data-pb-star-rating]"; +const STAR_RATING_INPUT_ID = "star-rating-input"; + +export default class PbStarRating extends PbEnhancedElement { + static get selector() { + return STAR_RATING_SELECTOR; + } + + connect() { + this.element.addEventListener("click", (event) => { + const clickedStarId = event.currentTarget.id; + this.updateStarColors(clickedStarId); + this.updateHiddenInputValue(clickedStarId); + }); + } + + updateStarColors(clickedStarId) { + const allStars = document.querySelectorAll(STAR_RATING_SELECTOR); + + allStars.forEach(star => { + const starId = star.id; + const icon = star.querySelector(".interactive-star-icon"); + + if (icon) { + if (starId <= clickedStarId) { + if (star.classList.contains("yellow_star")) { + icon.classList.add("yellow-star-selected"); + } else if (star.classList.contains("primary_star")) { + icon.classList.add("primary-star-selected"); + } else if (star.classList.contains("suble_star_light")) { + icon.classList.add("suble-star-selected"); + } else { + icon.classList.add("yellow-star-selected"); + } + } else { + icon.classList.remove("yellow-star-selected", "primary-star-selected", "suble-star-selected"); + } + } + }); + } + + updateHiddenInputValue(value) { + const hiddenInput = document.getElementById(STAR_RATING_INPUT_ID); + if (hiddenInput) { + hiddenInput.value = value; + } + } +} diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb b/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb index 21703f56ba..25814366d3 100644 --- a/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb +++ b/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb @@ -28,13 +28,33 @@ <% end %> <% end %> <%= pb_rails("flex", props: { }) do %> - <% object.star_count.times do %> - <%= pb_rails("icon", props: { classname: "#{star_color} pb_star_#{size}" , custom_icon: Playbook::Engine.root.join(star_svg_path) } ) %> - <% end %> - <% object.empty_stars.times do %> - <%= pb_rails("icon", props: { classname: "#{background_star_color} pb_star_#{size}", custom_icon: Playbook::Engine.root.join(background_star_path) } ) %> + + <% if object.variant == "display" %> + + <% object.star_count.times do %> + <%= pb_rails("icon", props: { classname: "#{star_color} pb_star_#{size}" , custom_icon: Playbook::Engine.root.join(star_svg_path) } ) %> + <% end %> + <% object.empty_stars.times do %> + <%= pb_rails("icon", props: { classname: "#{background_star_color} pb_star_#{size}", custom_icon: Playbook::Engine.root.join(background_star_path) } ) %> + <% end %> + + <% else %> + <%= pb_rails("flex", props: { orientation: "column" }) do %> + <% if object.label.present? %> + <%= pb_rails("caption", props: {text: object.label, margin_bottom:"xs"}) %> + <% end %> + <input type="hidden" id="star-rating-input" value="" name="<%= object.name %>"/> + <%= pb_rails("flex", props: { orientation: "row" }) do %> + <% object.denominator.times do |index| %> + <div data-pb-star-rating id="<%= index + 1 %>" class="<%= star_color %>"> + <%= pb_rails("icon", props: { classname: "#{background_star_color} pb_star_#{size} interactive-star-icon", custom_icon: Playbook::Engine.root.join(background_star_path)} ) %> + </div> + <% end %> + <% end %> + <% end %> <% end %> <% end %> + <% if layout_option == "onestar" %> <%= content_tag(:div, class: "pb_star_rating_number_#{size}") do %> <% case object.size %> diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.rb b/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.rb index a6320b5400..d1c8536a5f 100644 --- a/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.rb +++ b/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.rb @@ -25,6 +25,12 @@ class StarRating < Playbook::KitBase values: %w[fill outline], default: "fill" + prop :variant, type: Playbook::Props::Enum, + values: %w[display interactive], + default: "display" + prop :label, type: Playbook::Props::String + prop :name, type: Playbook::Props::String + def one_decimal_rating rating.to_f.round(1) end diff --git a/playbook/app/pb_kits/playbook/pb_table/_table.tsx b/playbook/app/pb_kits/playbook/pb_table/_table.tsx index 1759613343..d3bddeb721 100755 --- a/playbook/app/pb_kits/playbook/pb_table/_table.tsx +++ b/playbook/app/pb_kits/playbook/pb_table/_table.tsx @@ -33,7 +33,7 @@ type TableProps = { verticalBorder?: boolean, } & GlobalProps -const Table = (props: TableProps) => { +const Table = (props: TableProps): React.ReactElement => { const { aria = {}, children, diff --git a/playbook/app/pb_kits/playbook/pb_table/index.ts b/playbook/app/pb_kits/playbook/pb_table/index.ts index b50aa35dc2..8eb378752c 100644 --- a/playbook/app/pb_kits/playbook/pb_table/index.ts +++ b/playbook/app/pb_kits/playbook/pb_table/index.ts @@ -1,19 +1,19 @@ import PbEnhancedElement from '../pb_enhanced_element' export default class PbTable extends PbEnhancedElement { - static get selector() { + static get selector(): string { return '.table-responsive-collapse' } - connect() { + connect(): void { const tables = document.querySelectorAll('.table-responsive-collapse'); // Each Table [].forEach.call(tables, (table: HTMLTableElement) => { // Header Titles - let headers: string[] = []; + const headers: string[] = []; [].forEach.call(table.querySelectorAll('th'), (header: HTMLTableCellElement) => { - let colSpan = header.colSpan + const colSpan = header.colSpan for (let i = 0; i < colSpan; i++) { headers.push(header.textContent.replace(/\r?\n|\r/, '')); } diff --git a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_body.tsx b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_body.tsx index c347e3eb34..aa503d83b4 100644 --- a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_body.tsx +++ b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_body.tsx @@ -17,7 +17,7 @@ type TableBodyPropTypes = { tag?: "table" | "div"; }; -const TableBody = (props: TableBodyPropTypes) => { +const TableBody = (props: TableBodyPropTypes): React.ReactElement => { const { aria = {}, children, diff --git a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_cell.tsx b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_cell.tsx index 4d75ed8d15..5138bd3c2d 100644 --- a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_cell.tsx +++ b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_cell.tsx @@ -18,7 +18,7 @@ type TableCellPropTypes = { text?: string }; -const TableCell = (props: TableCellPropTypes) => { +const TableCell = (props: TableCellPropTypes): React.ReactElement => { const { aria = {}, children, diff --git a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_head.tsx b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_head.tsx index d97902f484..32b1627372 100644 --- a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_head.tsx +++ b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_head.tsx @@ -17,7 +17,7 @@ type TableHeadPropTypes = { tag?: "table" | "div"; }; -const TableHead = (props: TableHeadPropTypes) => { +const TableHead = (props: TableHeadPropTypes): React.ReactElement => { const { aria = {}, children, diff --git a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_header.tsx b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_header.tsx index 1d6609f0ea..4c81c6a7ab 100644 --- a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_header.tsx +++ b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_header.tsx @@ -18,7 +18,7 @@ type TableHeaderPropTypes = { text?: string; }; -const TableHeader = (props: TableHeaderPropTypes) => { +const TableHeader = (props: TableHeaderPropTypes): React.ReactElement => { const { aria = {}, children, diff --git a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_row.tsx b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_row.tsx index 0869719dee..3627f7f16a 100644 --- a/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_row.tsx +++ b/playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_row.tsx @@ -19,7 +19,7 @@ type TableRowPropTypes = { tag?: "table" | "div"; }; -const TableRow = (props: TableRowPropTypes) => { +const TableRow = (props: TableRowPropTypes): React.ReactElement => { const { aria = {}, children, diff --git a/playbook/app/pb_kits/playbook/pb_table/table.test.js b/playbook/app/pb_kits/playbook/pb_table/table.test.js index 3f0e8ad377..03a1a94196 100644 --- a/playbook/app/pb_kits/playbook/pb_table/table.test.js +++ b/playbook/app/pb_kits/playbook/pb_table/table.test.js @@ -1,3 +1,5 @@ +/* eslint-disable react/no-multi-comp */ + import React from "react"; import { ensureAccessible, renderKit, render, screen } from "../utilities/test-utils" diff --git a/playbook/app/pb_kits/playbook/pb_text_input/_text_input.tsx b/playbook/app/pb_kits/playbook/pb_text_input/_text_input.tsx index 654f544544..eee9a28704 100755 --- a/playbook/app/pb_kits/playbook/pb_text_input/_text_input.tsx +++ b/playbook/app/pb_kits/playbook/pb_text_input/_text_input.tsx @@ -107,7 +107,7 @@ const TextInput = (props: TextInputProps, ref: React.LegacyRef<HTMLInputElement> required={required} type={type} value={value} - />) + />) ) const addOnInput = ( diff --git a/playbook/app/pb_kits/playbook/pb_text_input/docs/_text_input_default.jsx b/playbook/app/pb_kits/playbook/pb_text_input/docs/_text_input_default.jsx index 33d4e60c53..e350a1d628 100755 --- a/playbook/app/pb_kits/playbook/pb_text_input/docs/_text_input_default.jsx +++ b/playbook/app/pb_kits/playbook/pb_text_input/docs/_text_input_default.jsx @@ -5,12 +5,12 @@ import TextInput from '../../pb_text_input/_text_input' import Title from '../../pb_title/_title' const TextInputDefault = (props) => { + const [firstName, setFirstName] = useState('') const handleOnChangeFirstName = ({ target }) => { setFirstName(target.value) } const ref = React.createRef() - const [firstName, setFirstName] = useState('') const [formFields, setFormFields] = useState({ firstName: 'Jane', lastName: 'Doe', diff --git a/playbook/app/pb_kits/playbook/pb_textarea/_textarea.tsx b/playbook/app/pb_kits/playbook/pb_textarea/_textarea.tsx index 4c33104ab2..276c6f6290 100644 --- a/playbook/app/pb_kits/playbook/pb_textarea/_textarea.tsx +++ b/playbook/app/pb_kits/playbook/pb_textarea/_textarea.tsx @@ -52,14 +52,15 @@ const Textarea = ({ label, maxCharacters, name, + // eslint-disable-next-line @typescript-eslint/no-empty-function onChange = () => {}, placeholder, required, rows = 4, value, ...props -}: TextareaProps, ref: any) => { - ref = useRef<HTMLTextAreaElement>(null) +}: TextareaProps) => { + const ref = useRef<HTMLTextAreaElement>(null) useEffect(() => { if (ref.current && resize === 'auto') { PbTextarea.addMatch(ref.current) @@ -71,58 +72,75 @@ const Textarea = ({ const resizeClass = `resize_${resize}` const classes = classnames('pb_textarea_kit', errorClass, inlineClass, resizeClass, globalProps(props), className) const noCount = typeof characterCount !== 'undefined' - const ariaProps: {[key: string]: any} = buildAriaProps(aria) - const dataProps: {[key: string]: any} = buildDataProps(data) + const ariaProps: {[key: string]: string} = buildAriaProps(aria) + const dataProps: {[key: string]: string} = buildDataProps(data) const htmlProps = buildHtmlProps(htmlOptions) - const characterCounter = () => { - return maxCharacters && characterCount ? `${checkIfZero(characterCount)} / ${maxCharacters}` : `${checkIfZero(characterCount)}` - } - const checkIfZero = (characterCount: string | number) => { return characterCount == 0 ? characterCount.toString() : characterCount } + const characterCounter = () => { + return maxCharacters && characterCount ? `${checkIfZero(characterCount)} / ${maxCharacters}` : `${checkIfZero(characterCount)}` + } return ( <div - {...ariaProps} - {...dataProps} - {...htmlProps} - className={classes} + {...ariaProps} + {...dataProps} + {...htmlProps} + className={classes} > <Caption text={label} /> {children || ( <textarea - className="pb_textarea_kit" - disabled={disabled} - name={name} - onChange={onChange} - placeholder={placeholder} - ref={ref} - required={required} - rows={rows} - value={value} - {...props} + className="pb_textarea_kit" + disabled={disabled} + name={name} + onChange={onChange} + placeholder={placeholder} + ref={ref} + required={required} + rows={rows} + value={value} + {...props} /> )} {error ? ( <> {characterCount ? ( - <Flex spacing="between" vertical="center"> + <Flex + spacing="between" + vertical="center" + > <FlexItem> - <Body margin="none" status="negative" text={error} /> + <Body + margin="none" + status="negative" + text={error} + /> </FlexItem> <FlexItem> - <Caption margin="none" size="xs" text={characterCounter()} /> + <Caption + margin="none" + size="xs" + text={characterCounter()} + /> </FlexItem> </Flex> ) : ( - <Body status="negative" text={error} /> + <Body + status="negative" + text={error} + /> )} </> ) : ( noCount && ( - <Caption margin="none" size="xs" text={characterCounter()} /> + <Caption + margin="none" + size="xs" + text={characterCounter()} + /> ) )} </div> diff --git a/playbook/app/pb_kits/playbook/pb_textarea/index.tsx b/playbook/app/pb_kits/playbook/pb_textarea/index.tsx index cab431c3ed..2aebc51d87 100644 --- a/playbook/app/pb_kits/playbook/pb_textarea/index.tsx +++ b/playbook/app/pb_kits/playbook/pb_textarea/index.tsx @@ -3,16 +3,16 @@ import PbEnhancedElement from '../pb_enhanced_element' export default class PbTextarea extends PbEnhancedElement { style: {[key: string]: string} scrollHeight: string - static get selector() { + static get selector(): string { return '.resize_auto textarea' } - onInput() { + onInput(): void { this.style.height = 'auto' this.style.height = (this.scrollHeight) + 'px' } - connect() { + connect(): void { this.element.setAttribute('style', 'height:' + (this.element.scrollHeight) + 'px;overflow-y:hidden;') this.element.addEventListener('input', this.onInput, false) } diff --git a/playbook/app/pb_kits/playbook/pb_time/_time.tsx b/playbook/app/pb_kits/playbook/pb_time/_time.tsx index 3d36c796f2..f59d32bb9a 100644 --- a/playbook/app/pb_kits/playbook/pb_time/_time.tsx +++ b/playbook/app/pb_kits/playbook/pb_time/_time.tsx @@ -24,7 +24,7 @@ type TimeProps = { unstyled?: boolean; } & GlobalProps -const Time = (props: TimeProps) => { +const Time = (props: TimeProps): React.ReactElement => { const { align, className, @@ -47,8 +47,8 @@ const Time = (props: TimeProps) => { return ( <div - {...htmlProps} - className={classes} + {...htmlProps} + className={classes} > {showIcon && ( unstyled diff --git a/playbook/app/pb_kits/playbook/pb_time_range_inline/_time_range_inline.tsx b/playbook/app/pb_kits/playbook/pb_time_range_inline/_time_range_inline.tsx index 6b6ec65104..77d0247b90 100644 --- a/playbook/app/pb_kits/playbook/pb_time_range_inline/_time_range_inline.tsx +++ b/playbook/app/pb_kits/playbook/pb_time_range_inline/_time_range_inline.tsx @@ -36,7 +36,7 @@ const dateTimeIso = (dateValue: Date) => { return DateTime.toIso(dateValue) } -const TimeRangeInline = (props: TimeRangeInlineProps) => { +const TimeRangeInline = (props: TimeRangeInlineProps): React.ReactElement => { const { aria = {}, className, diff --git a/playbook/app/pb_kits/playbook/pb_timeline/_item.tsx b/playbook/app/pb_kits/playbook/pb_timeline/_item.tsx index 7f7fc6d839..3d6ef9eafd 100644 --- a/playbook/app/pb_kits/playbook/pb_timeline/_item.tsx +++ b/playbook/app/pb_kits/playbook/pb_timeline/_item.tsx @@ -26,7 +26,7 @@ const TimelineItem = ({ iconColor = 'default', lineStyle = 'solid', ...props -}: ItemProps) => { +}: ItemProps): React.ReactElement => { const timelineItemCss = buildCss('pb_timeline_item_kit', lineStyle) const htmlProps = buildHtmlProps(htmlOptions) diff --git a/playbook/app/pb_kits/playbook/pb_timeline/_timeline.tsx b/playbook/app/pb_kits/playbook/pb_timeline/_timeline.tsx index b42db1915f..e58d9f8acb 100644 --- a/playbook/app/pb_kits/playbook/pb_timeline/_timeline.tsx +++ b/playbook/app/pb_kits/playbook/pb_timeline/_timeline.tsx @@ -27,7 +27,7 @@ const Timeline = ({ orientation = 'horizontal', showDate = false, ...props -}: TimelineProps) => { +}: TimelineProps): React.ReactElement => { const ariaProps = buildAriaProps(aria) const dataProps = buildDataProps(data) const htmlProps = buildHtmlProps(htmlOptions) diff --git a/playbook/app/pb_kits/playbook/pb_title_detail/_title_detail.tsx b/playbook/app/pb_kits/playbook/pb_title_detail/_title_detail.tsx index c7b7e638fc..bf5ee0f9e9 100644 --- a/playbook/app/pb_kits/playbook/pb_title_detail/_title_detail.tsx +++ b/playbook/app/pb_kits/playbook/pb_title_detail/_title_detail.tsx @@ -18,7 +18,7 @@ type TitleDetailProps = { title: string, } & GlobalProps -const TitleDetail = (props: TitleDetailProps) => { +const TitleDetail = (props: TitleDetailProps): React.ReactElement => { const { align = "left", aria = {}, @@ -37,19 +37,19 @@ const TitleDetail = (props: TitleDetailProps) => { return ( <div - {...ariaProps} - {...dataProps} - {...htmlProps} - className={classnames(pbCss, globalProps(props), className)} - id={id} + {...ariaProps} + {...dataProps} + {...htmlProps} + className={classnames(pbCss, globalProps(props), className)} + id={id} > <Title - size={4} - text={title} + size={4} + text={title} /> <Body - color="light" - text={detail} + color="light" + text={detail} /> </div> ) diff --git a/playbook/app/pb_kits/playbook/pb_toggle/_toggle.tsx b/playbook/app/pb_kits/playbook/pb_toggle/_toggle.tsx index 9f28ff18fe..938c67512d 100644 --- a/playbook/app/pb_kits/playbook/pb_toggle/_toggle.tsx +++ b/playbook/app/pb_kits/playbook/pb_toggle/_toggle.tsx @@ -42,7 +42,7 @@ const Toggle = ({ size = 'sm', value, ...props -}: Props) => { +}: Props): React.ReactElement => { const ariaProps = buildAriaProps(aria) const dataProps = buildDataProps(data) const htmlProps = buildHtmlProps(htmlOptions) diff --git a/playbook/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx b/playbook/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx index d1216eef36..ae8e2667d5 100644 --- a/playbook/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +++ b/playbook/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx @@ -50,8 +50,8 @@ const Tooltip = forwardRef((props: TooltipProps, ref: ForwardedRef<unknown>): Re ...rest } = props - const dataProps: { [key: string]: any } = buildDataProps(data) - const ariaProps: { [key: string]: any } = buildAriaProps(aria) + const dataProps: { [key: string]: string } = buildDataProps(data) + const ariaProps: { [key: string]: string } = buildAriaProps(aria) const htmlProps = buildHtmlProps(htmlOptions) const css = classnames( diff --git a/playbook/app/pb_kits/playbook/pb_treemap_chart/_treemap_chart.tsx b/playbook/app/pb_kits/playbook/pb_treemap_chart/_treemap_chart.tsx index 60978e0b56..1e50c7fbeb 100644 --- a/playbook/app/pb_kits/playbook/pb_treemap_chart/_treemap_chart.tsx +++ b/playbook/app/pb_kits/playbook/pb_treemap_chart/_treemap_chart.tsx @@ -52,7 +52,7 @@ const TreemapChart = ({ tooltipHtml = '<span style="font-weight: bold; color:{point.color};">●</span>{point.name}: <b>{point.value}</b>', type = "treemap", ...props -}: TreemapChartProps) => { +}: TreemapChartProps): React.ReactElement => { const ariaProps = buildAriaProps(aria) const dataProps = buildDataProps(data) @@ -98,7 +98,6 @@ const TreemapChart = ({ const [options, setOptions] = useState({}); useEffect(() => { - setOptions(merge(staticOptions, customOptions)); }, [chartData]); diff --git a/playbook/app/pb_kits/playbook/pb_treemap_chart/treemapChart.test.js b/playbook/app/pb_kits/playbook/pb_treemap_chart/treemapChart.test.js index bf629b9bef..d159b7027e 100644 --- a/playbook/app/pb_kits/playbook/pb_treemap_chart/treemapChart.test.js +++ b/playbook/app/pb_kits/playbook/pb_treemap_chart/treemapChart.test.js @@ -1,3 +1,5 @@ +/* eslint-disable no-console */ + import React from 'react'; import { render, screen } from '../utilities/test-utils'; import TreemapChart from './_treemap_chart'; diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx index f79fc38fc5..351e97a759 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx @@ -42,8 +42,8 @@ type TypeaheadProps = { id?: string, label?: string, loadOptions?: string | Noop, - getOptionLabel?: string | (() => any), - getOptionValue?: string | (() => any), + getOptionLabel?: string | (() => string), + getOptionValue?: string | (() => string), name?: string, } @@ -77,7 +77,7 @@ const Typeahead = ({ id, loadOptions = noop, ...props -}: TypeaheadProps) => { +}: TypeaheadProps): React.ReactElement => { const selectProps = { cacheOptions: true, components: { diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx index 9de04859a6..18468262b9 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx @@ -1,16 +1,16 @@ import React, { useEffect } from 'react' import { components } from 'react-select' -const ClearContainer = (props: any) => { +const ClearContainer = (props: any): React.ReactElement => { const { selectProps, clearValue } = props useEffect(() => { document.addEventListener(`pb-typeahead-kit-${selectProps.id}:clear`, clearValue) - }, [true]) + }, []) return ( <components.ClearIndicator - className="clear_indicator" - {...props} + className="clear_indicator" + {...props} /> ) } diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/components/Control.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/components/Control.tsx index 363e629c71..fe02b40fe5 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/components/Control.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/components/Control.tsx @@ -5,20 +5,24 @@ import Flex from '../../pb_flex/_flex' import TextInput from '../../pb_text_input/_text_input' type Props = { - selectProps: any, + selectProps: { + dark?: boolean, + label: string, + error?: string, + }, } -const TypeaheadControl = (props: Props) => ( +const TypeaheadControl = (props: Props): React.ReactElement => ( <div className="pb_typeahead_wrapper"> <TextInput - dark={props.selectProps.dark} - error={props.selectProps.error} - label={props.selectProps.label} + dark={props.selectProps.dark} + error={props.selectProps.error} + label={props.selectProps.label} > <Flex> <components.Control - className="text_input" - {...props} + className="text_input" + {...props} /> </Flex> </TextInput> diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/components/IndicatorsContainer.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/components/IndicatorsContainer.tsx index 28a8108c01..488b9851cb 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/components/IndicatorsContainer.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/components/IndicatorsContainer.tsx @@ -1,10 +1,15 @@ import React from 'react' import { components } from 'react-select' -const IndicatorsContainer = (props: any) => ( +type IndicatorsContainerProps = { + children: React.ReactNode, +} + + +const IndicatorsContainer = (props: IndicatorsContainerProps): React.ReactElement => ( <components.IndicatorsContainer - className="text_input_indicators" - {...props} + className="text_input_indicators" + {...props} /> ) diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/components/MenuList.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/components/MenuList.tsx index a83e3e630e..b60408d77d 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/components/MenuList.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/components/MenuList.tsx @@ -1,7 +1,12 @@ import React from 'react' import { components } from 'react-select' -const MenuList = (props: any) => { +type MenuListProps = { + children: React.ReactNode, + footer: React.ReactNode, +} + +const MenuList = (props: MenuListProps): React.ReactElement => { return ( <components.MenuList {...props}> {props.children} diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx index e3ce62ef5f..fc5781ec9f 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx @@ -7,12 +7,11 @@ import { SelectValueType } from '../_typeahead' type Props = { data: SelectValueType, - multiValueTemplate: any, removeProps: any, selectProps: any, } -const MultiValue = (props: Props) => { +const MultiValue = (props: Props): React.ReactElement => { const { removeProps } = props const { imageUrl, label } = props.data const { multiKit } = props.selectProps @@ -27,36 +26,36 @@ const MultiValue = (props: Props) => { return ( <components.MultiValueContainer - className="text_input_multivalue_container" - {...props} + className="text_input_multivalue_container" + {...props} > {multiKit === 'badge' && <Badge - closeProps={removeProps} - removeIcon - text={label} - variant="primary" + closeProps={removeProps} + removeIcon + text={label} + variant="primary" /> } {multiKit !== 'badge' && imageUrl && <FormPill - avatarUrl={imageUrl} - closeProps={removeProps} - marginRight="xs" - name={label} - size={multiKit === 'smallPill' ? 'small' : ''} - text='' + avatarUrl={imageUrl} + closeProps={removeProps} + marginRight="xs" + name={label} + size={multiKit === 'smallPill' ? 'small' : ''} + text='' /> } {multiKit !== 'badge' && !imageUrl && <FormPill - closeProps={removeProps} - marginRight="xs" - name='' - size={multiKit === 'smallPill' ? 'small' : ''} - text={label} + closeProps={removeProps} + marginRight="xs" + name='' + size={multiKit === 'smallPill' ? 'small' : ''} + text={label} /> } </components.MultiValueContainer> diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/components/Option.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/components/Option.tsx index f31b66561e..33cdc63d19 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/components/Option.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/components/Option.tsx @@ -3,7 +3,7 @@ import { components } from 'react-select' import User from '../../pb_user/_user' -const Option = (props: any) => { +const Option = (props: any): React.ReactElement => { const { imageUrl, } = props.data @@ -14,11 +14,11 @@ const Option = (props: any) => { <> {!valueComponent && imageUrl && <User - align="left" - avatarUrl={imageUrl} - dark={props.selectProps.dark} - name={props.label} - orientation="horizontal" + align="left" + avatarUrl={imageUrl} + dark={props.selectProps.dark} + name={props.label} + orientation="horizontal" /> } diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/components/Placeholder.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/components/Placeholder.tsx index 9288eafc1e..60d67ebfea 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/components/Placeholder.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/components/Placeholder.tsx @@ -4,19 +4,19 @@ import { components } from 'react-select' import Flex from '../../pb_flex/_flex' import Icon from '../../pb_icon/_icon' -const Placeholder = (props: any) => ( +const Placeholder = (props: any): React.ReactElement => ( <> <Flex - align="center" - className="placeholder" + align="center" + className="placeholder" > <components.IndicatorsContainer - {...props} + {...props} /> {props.selectProps.plusIcon && <Icon - className="typeahead-plus-icon" - icon="plus" + className="typeahead-plus-icon" + icon="plus" /> } </Flex> diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx b/playbook/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx index cc27bc0c2a..e16f31a447 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx @@ -1,10 +1,10 @@ import React from 'react' import { components } from 'react-select' -const ValueContainer = (props: any) => ( +const ValueContainer = (props: any): React.ReactElement => ( <components.ValueContainer - className="text_input_value_container" - {...props} + className="text_input_value_container" + {...props} /> ) diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_custom_menu_list.jsx b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_custom_menu_list.jsx index db7659319a..74e9ccc4c9 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_custom_menu_list.jsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_custom_menu_list.jsx @@ -1,3 +1,5 @@ +/* eslint-disable react/no-multi-comp */ + import React, { useState } from 'react' import { diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_default.html.erb b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_default.html.erb index 8c7b894a95..563e70a4ff 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_default.html.erb +++ b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_default.html.erb @@ -1,63 +1,28 @@ -<%= pb_rails("typeahead", props: { - label: "user", - name: :foo, - data: { typeahead_example: true }, - input_options: { - classname: "my-typeahead-class", - data: { - typeahead_testing: "data field test" - }, - id: "typeahead-input-id-test", - }, -})%> - -<br><br><br> - -<%= pb_rails("card", props: { padding: "xl", data: { typeahead_example_selected_option: true } }) do %> - <%= pb_rails("body") do %> - Use the above input to search for users on Github, and see the results as you type. - <% end %> - - <%= pb_rails("body") do %> - When you make a selection, you will see it appear in the list below - <% end %> +<% + options = [ + { label: 'Orange', value: '#FFA500' }, + { label: 'Red', value: '#FF0000' }, + { label: 'Green', value: '#00FF00' }, + { label: 'Blue', value: '#0000FF' }, + ] +%> - <div data-selected-option></div> -<% end %> - -<template data-typeahead-example-result-option> - <%= pb_rails("user", props: { - name: tag(:slot, name: "name"), - orientation: "horizontal", - align: "left", - avatar_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII=", - avatar: true - }) %> -</template> +<%= pb_rails("typeahead", props: { + id: "typeahead-default", + placeholder: "All Colors", + options: options, + label: "Colors", + name: :foo, + is_multi: false + }) +%> <%= javascript_tag defer: "defer" do %> - document.addEventListener("pb-typeahead-kit-search", function(event) { - if (!event.target.dataset || !event.target.dataset.typeaheadExample) return; - - fetch(`https://api.github.com/search/users?q=${encodeURIComponent(event.detail.searchingFor)}`) - .then(response => response.json()) - .then((result) => { - const resultOptionTemplate = document.querySelector("[data-typeahead-example-result-option]") - - event.detail.setResults((result.items || []).map((user) => { - const wrapper = resultOptionTemplate.content.cloneNode(true) - wrapper.querySelector('slot[name="name"]').replaceWith(user.login) - wrapper.querySelector('img').dataset.src = user.avatar_url - return wrapper - })) - }) + document.addEventListener("pb-typeahead-kit-typeahead-default-result-option-select", function(event) { + console.log('Single Option selected') + console.dir(event.detail) }) - - - document.addEventListener("pb-typeahead-kit-result-option-selected", function(event) { - if (!event.target.dataset.typeaheadExample) return; - - document.querySelector("[data-typeahead-example-selected-option] [data-selected-option]").innerHTML = "" - document.querySelector("[data-typeahead-example-selected-option] [data-selected-option]").innerHTML = event.detail.selected.innerHTML + document.addEventListener("pb-typeahead-kit-typeahead-default-result-clear", function() { + console.log('All options cleared') }) <% end %> diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_async.jsx b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_async.jsx index f220cf9a9f..e701e574bd 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_async.jsx +++ b/playbook/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_async.jsx @@ -47,8 +47,6 @@ const promiseOptions = (inputValue) => const TypeaheadWithPillsAsync = (props) => { const [users, setUsers] = useState([]) - const handleOnChange = (value) => setUsers(formatUsers(value)) - const formatValue = (users) => formatUsers(users) const formatUsers = (users) => { const results = () => (users.map((user) => { if (Object.keys(user)[0] === 'name' || Object.keys(user)[1] === 'id'){ @@ -59,6 +57,8 @@ const TypeaheadWithPillsAsync = (props) => { })) return results() } + const handleOnChange = (value) => setUsers(formatUsers(value)) + const formatValue = (users) => formatUsers(users) return ( <> diff --git a/playbook/app/pb_kits/playbook/pb_typeahead/index.ts b/playbook/app/pb_kits/playbook/pb_typeahead/index.ts index a246f88770..c342f52989 100644 --- a/playbook/app/pb_kits/playbook/pb_typeahead/index.ts +++ b/playbook/app/pb_kits/playbook/pb_typeahead/index.ts @@ -4,24 +4,24 @@ import { debounce } from 'lodash' export default class PbTypeahead extends PbEnhancedElement { _searchInput: HTMLInputElement _resultsElement: HTMLElement - _debouncedSearch: Function + _debouncedSearch: () => void _resultsLoadingIndicator: HTMLElement _resultOptionTemplate: HTMLElement _resultsOptionCache: Map<string, Array<DocumentFragment>> _searchContext: string - static get selector() { + static get selector(): string { return '[data-pb-typeahead-kit]' } - connect() { + connect(): void { this.element.addEventListener('keydown', (event: KeyboardEvent) => this.handleKeydown(event)) - this.searchInput.addEventListener('focus', () => this.debouncedSearch()) - this.searchInput.addEventListener('input', () => this.debouncedSearch()) + this.searchInput.addEventListener('focus', () => this.debouncedSearch) + this.searchInput.addEventListener('input', () => this.debouncedSearch) this.resultsElement.addEventListener('click', (event: MouseEvent) => this.optionSelected(event)) } - handleKeydown(event: KeyboardEvent) { + handleKeydown(event: KeyboardEvent): void { if (event.key === 'ArrowUp') { event.preventDefault() this.focusPreviousOption() @@ -31,7 +31,7 @@ export default class PbTypeahead extends PbEnhancedElement { } } - search() { + search(): void { if (this.searchTerm.length < parseInt(this.searchTermMinimumLength)) return this.clearResults() this.toggleResultsLoadingIndicator(true) @@ -49,7 +49,7 @@ export default class PbTypeahead extends PbEnhancedElement { this.element.dispatchEvent(new CustomEvent('pb-typeahead-kit-search', { bubbles: true, detail: search })) } - resultsCacheUpdate(searchTerm: string, searchContext: string, results: Array<DocumentFragment>) { + resultsCacheUpdate(searchTerm: string, searchContext: string, results: Array<DocumentFragment>): void { const searchTermAndContext = this.cacheKeyFor(searchTerm, searchContext) if (this.resultsOptionCache.has(searchTermAndContext)) this.resultsOptionCache.delete(searchTermAndContext) if (this.resultsOptionCache.size > 32) this.resultsOptionCache.delete(this.resultsOptionCache.keys().next().value) @@ -58,18 +58,18 @@ export default class PbTypeahead extends PbEnhancedElement { this.showResults() } - resultsCacheClear() { + resultsCacheClear(): void { this.resultsOptionCache.clear() } - get debouncedSearch() { + get debouncedSearch(): void { return this._debouncedSearch = ( this._debouncedSearch || debounce(this.search, parseInt(this.searchDebounceTimeout)).bind(this) ) } - showResults() { + showResults(): void { if (!this.resultsOptionCache.has(this.searchTermAndContext)) return this.toggleResultsLoadingIndicator(false) @@ -82,7 +82,7 @@ export default class PbTypeahead extends PbEnhancedElement { } } - optionSelected(event: MouseEvent) { + optionSelected(event: MouseEvent): void { const resultOption = (event.target as Element).closest('[data-result-option-item]') if (!resultOption) return @@ -93,7 +93,7 @@ export default class PbTypeahead extends PbEnhancedElement { this.element.dispatchEvent(new CustomEvent('pb-typeahead-kit-result-option-selected', { bubbles: true, detail: { selected: resultOption, typeahead: this } })) } - clearResults() { + clearResults(): void { this.resultsElement.innerHTML = '' } @@ -103,7 +103,7 @@ export default class PbTypeahead extends PbEnhancedElement { return resultOption } - focusPreviousOption() { + focusPreviousOption(): void { const currentIndex = this.resultOptionItems.indexOf(this.currentSelectedResultOptionItem) const previousIndex = currentIndex - 1 const previousOptionItem = ( @@ -113,7 +113,7 @@ export default class PbTypeahead extends PbEnhancedElement { (previousOptionItem as HTMLElement).focus() } - focusNextOption() { + focusNextOption(): void { const currentIndex = this.resultOptionItems.indexOf(this.currentSelectedResultOptionItem) const nextIndex = currentIndex + 1 const nextOptionItem = ( @@ -123,23 +123,23 @@ export default class PbTypeahead extends PbEnhancedElement { (nextOptionItem as HTMLElement).focus() } - get resultOptionItems() { + get resultOptionItems(): HTMLElement[] { return Array.from(this.resultsElement.querySelectorAll('[data-result-option-item]')) } - get currentSelectedResultOptionItem() { + get currentSelectedResultOptionItem(): HTMLElement | null { return document.activeElement.closest('[data-result-option-item]') } - get searchInput() { + get searchInput(): HTMLInputElement | null { return this._searchInput = (this._searchInput || this.element.querySelector('input[type="search"]')) } - get searchTerm() { + get searchTerm(): string { return this.searchInput.value } - get searchContext() { + get searchContext(): string | null { if (this._searchContext) return this._searchContext const selector = (this.element as HTMLElement).dataset.searchContextValueSelector @@ -151,35 +151,35 @@ export default class PbTypeahead extends PbEnhancedElement { return null } - set searchContext(value) { + set searchContext(value: string) { this._searchContext = value } - get searchTermAndContext() { + get searchTermAndContext(): string { return this.cacheKeyFor(this.searchTerm, this.searchContext) } - cacheKeyFor(searchTerm: string, searchContext: string) { + cacheKeyFor(searchTerm: string, searchContext: string): string { return [searchTerm, JSON.stringify(searchContext)].join() } - searchInputClear() { + searchInputClear(): void { this.searchInput.value = '' } - get searchTermMinimumLength() { + get searchTermMinimumLength(): string | undefined { return (this.element as HTMLElement).dataset.pbTypeaheadKitSearchTermMinimumLength } - get searchDebounceTimeout() { + get searchDebounceTimeout(): string | undefined { return (this.element as HTMLElement).dataset.pbTypeaheadKitSearchDebounceTimeout } - get resultsElement() { + get resultsElement(): HTMLElement | null { return this._resultsElement = (this._resultsElement || this.element.querySelector('[data-pb-typeahead-kit-results]')) } - get resultOptionTemplate() { + get resultOptionTemplate(): HTMLElement | null { return this._resultOptionTemplate = ( this._resultOptionTemplate || this.element.querySelector('template[data-pb-typeahead-kit-result-option]') @@ -193,15 +193,15 @@ export default class PbTypeahead extends PbEnhancedElement { ) } - get resultsLoadingIndicator() { + get resultsLoadingIndicator(): HTMLElement | null { return this._resultsLoadingIndicator = ( this._resultsLoadingIndicator || this.element.querySelector('[data-pb-typeahead-kit-loading-indicator]') ) } - toggleResultsLoadingIndicator(visible: boolean) { - var visibilityProperty = '0' + toggleResultsLoadingIndicator(visible: boolean): void { + let visibilityProperty = '0' if (visible) visibilityProperty = '1' this.resultsLoadingIndicator.style.opacity = visibilityProperty } diff --git a/playbook/app/pb_kits/playbook/pb_user/_user.tsx b/playbook/app/pb_kits/playbook/pb_user/_user.tsx index 0c3922b54c..6bdb525246 100644 --- a/playbook/app/pb_kits/playbook/pb_user/_user.tsx +++ b/playbook/app/pb_kits/playbook/pb_user/_user.tsx @@ -26,7 +26,7 @@ type UserProps = { title?: string, } & GlobalProps -const User = (props: UserProps) => { +const User = (props: UserProps): React.ReactElement => { const { align = 'left', aria = {}, diff --git a/playbook/app/pb_kits/playbook/pb_user_badge/_user_badge.tsx b/playbook/app/pb_kits/playbook/pb_user_badge/_user_badge.tsx index 661be55a39..5be4b04b9b 100644 --- a/playbook/app/pb_kits/playbook/pb_user_badge/_user_badge.tsx +++ b/playbook/app/pb_kits/playbook/pb_user_badge/_user_badge.tsx @@ -15,7 +15,7 @@ type UserBadgeProps = { size?: "sm" | "md" | "lg", } -const UserBadge = (props: UserBadgeProps) => { +const UserBadge = (props: UserBadgeProps): React.ReactElement => { const { aria = {}, badge = 'million-dollar', @@ -38,11 +38,11 @@ const UserBadge = (props: UserBadgeProps) => { return ( <div - {...ariaProps} - {...dataProps} - {...htmlProps} - className={classes} - id={id} + {...ariaProps} + {...dataProps} + {...htmlProps} + className={classes} + id={id} > <div className="pb_user_badge_wrapper"> {image} diff --git a/playbook/app/pb_kits/playbook/pb_user_badge/badges/million-dollar.tsx b/playbook/app/pb_kits/playbook/pb_user_badge/badges/million-dollar.tsx index 9e6d49927d..75d859d1f1 100644 --- a/playbook/app/pb_kits/playbook/pb_user_badge/badges/million-dollar.tsx +++ b/playbook/app/pb_kits/playbook/pb_user_badge/badges/million-dollar.tsx @@ -1,356 +1,357 @@ import * as React from "react" -const MillionDollar = () => ( +const MillionDollar = (): React.ReactElement => ( <svg - viewBox="0 0 242.9 242.9" - xmlSpace="preserve" - xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 242.9 242.9" + xmlSpace="preserve" + xmlns="http://www.w3.org/2000/svg" > - <circle cx={121.5} - cy={121.5} - r={121.5} + <circle + cx={121.5} + cy={121.5} + r={121.5} /> <linearGradient - gradientUnits="userSpaceOnUse" - id="a" - x1={42.483} - x2={200.309} - y1={35.645} - y2={35.645} + gradientUnits="userSpaceOnUse" + id="a" + x1={42.483} + x2={200.309} + y1={35.645} + y2={35.645} > <stop - offset={0} - style={{ - stopColor: "#e6b711", - }} + offset={0} + style={{ + stopColor: "#e6b711", + }} /> <stop - offset={0.128} - style={{ - stopColor: "#eac23c", - }} + offset={0.128} + style={{ + stopColor: "#eac23c", + }} /> <stop - offset={0.278} - style={{ - stopColor: "#ec6", - }} + offset={0.278} + style={{ + stopColor: "#ec6", + }} /> <stop - offset={0.413} - style={{ - stopColor: "#f1d485", - }} + offset={0.413} + style={{ + stopColor: "#f1d485", + }} /> <stop - offset={0.527} - style={{ - stopColor: "#f2d998", - }} + offset={0.527} + style={{ + stopColor: "#f2d998", + }} /> <stop - offset={0.607} - style={{ - stopColor: "#f3db9f", - }} + offset={0.607} + style={{ + stopColor: "#f3db9f", + }} /> <stop - offset={1} - style={{ - stopColor: "#e6b711", - }} + offset={1} + style={{ + stopColor: "#e6b711", + }} /> </linearGradient> <path - d="M56.4 53.8c16.9-16.2 39.8-26.2 65-26.2 25.2 0 48.1 10 64.9 26.2h14c-19.1-22.2-47.4-36.3-78.9-36.3-31.5 0-59.8 14.1-78.9 36.3h13.9z" - style={{ - fill: "url(#a)", - }} + d="M56.4 53.8c16.9-16.2 39.8-26.2 65-26.2 25.2 0 48.1 10 64.9 26.2h14c-19.1-22.2-47.4-36.3-78.9-36.3-31.5 0-59.8 14.1-78.9 36.3h13.9z" + style={{ + fill: "url(#a)", + }} /> <linearGradient - gradientUnits="userSpaceOnUse" - id="b" - x1={17.407} - x2={44.897} - y1={132.179} - y2={132.179} + gradientUnits="userSpaceOnUse" + id="b" + x1={17.407} + x2={44.897} + y1={132.179} + y2={132.179} > <stop - offset={0} - style={{ - stopColor: "#e6b711", - }} + offset={0} + style={{ + stopColor: "#e6b711", + }} /> <stop - offset={0.128} - style={{ - stopColor: "#eac23c", - }} + offset={0.128} + style={{ + stopColor: "#eac23c", + }} /> <stop - offset={0.278} - style={{ - stopColor: "#ec6", - }} + offset={0.278} + style={{ + stopColor: "#ec6", + }} /> <stop - offset={0.413} - style={{ - stopColor: "#f1d485", - }} + offset={0.413} + style={{ + stopColor: "#f1d485", + }} /> <stop - offset={0.527} - style={{ - stopColor: "#f2d998", - }} + offset={0.527} + style={{ + stopColor: "#f2d998", + }} /> <stop - offset={0.607} - style={{ - stopColor: "#f3db9f", - }} + offset={0.607} + style={{ + stopColor: "#f3db9f", + }} /> <stop - offset={1} - style={{ - stopColor: "#e6b711", - }} + offset={1} + style={{ + stopColor: "#e6b711", + }} /> </linearGradient> <path - d="M44.9 175.7c-10.9-15.3-17.3-34-17.3-54.2 0-14.1 3.1-27.5 8.8-39.5H25.3c-5 12.2-7.8 25.5-7.8 39.5 0 22.7 7.3 43.8 19.8 60.9l7.6-6.7z" - style={{ - fill: "url(#b)", - }} + d="M44.9 175.7c-10.9-15.3-17.3-34-17.3-54.2 0-14.1 3.1-27.5 8.8-39.5H25.3c-5 12.2-7.8 25.5-7.8 39.5 0 22.7 7.3 43.8 19.8 60.9l7.6-6.7z" + style={{ + fill: "url(#b)", + }} /> <linearGradient - gradientUnits="userSpaceOnUse" - id="c" - x1={52.914} - x2={189.715} - y1={212.573} - y2={212.573} + gradientUnits="userSpaceOnUse" + id="c" + x1={52.914} + x2={189.715} + y1={212.573} + y2={212.573} > <stop - offset={0} - style={{ - stopColor: "#e6b711", - }} + offset={0} + style={{ + stopColor: "#e6b711", + }} /> <stop - offset={0.128} - style={{ - stopColor: "#eac23c", - }} + offset={0.128} + style={{ + stopColor: "#eac23c", + }} /> <stop - offset={0.278} - style={{ - stopColor: "#ec6", - }} + offset={0.278} + style={{ + stopColor: "#ec6", + }} /> <stop - offset={0.413} - style={{ - stopColor: "#f1d485", - }} + offset={0.413} + style={{ + stopColor: "#f1d485", + }} /> <stop - offset={0.527} - style={{ - stopColor: "#f2d998", - }} + offset={0.527} + style={{ + stopColor: "#f2d998", + }} /> <stop - offset={0.607} - style={{ - stopColor: "#f3db9f", - }} + offset={0.607} + style={{ + stopColor: "#f3db9f", + }} /> <stop - offset={1} - style={{ - stopColor: "#e6b711", - }} + offset={1} + style={{ + stopColor: "#e6b711", + }} /> </linearGradient> <path - d="M173 199.8c-14.8 9.8-32.5 15.5-51.6 15.5s-37-5.8-51.8-15.6H52.9c18.3 16.1 42.3 25.8 68.5 25.8 26.1 0 50-9.7 68.3-25.7H173z" - style={{ - fill: "url(#c)", - }} + d="M173 199.8c-14.8 9.8-32.5 15.5-51.6 15.5s-37-5.8-51.8-15.6H52.9c18.3 16.1 42.3 25.8 68.5 25.8 26.1 0 50-9.7 68.3-25.7H173z" + style={{ + fill: "url(#c)", + }} /> <linearGradient - gradientUnits="userSpaceOnUse" - id="d" - x1={31.123} - x2={207.766} - y1={136.159} - y2={136.159} + gradientUnits="userSpaceOnUse" + id="d" + x1={31.123} + x2={207.766} + y1={136.159} + y2={136.159} > <stop - offset={0} - style={{ - stopColor: "#e6b711", - }} + offset={0} + style={{ + stopColor: "#e6b711", + }} /> <stop - offset={0.128} - style={{ - stopColor: "#eac23c", - }} + offset={0.128} + style={{ + stopColor: "#eac23c", + }} /> <stop - offset={0.278} - style={{ - stopColor: "#ec6", - }} + offset={0.278} + style={{ + stopColor: "#ec6", + }} /> <stop - offset={0.413} - style={{ - stopColor: "#f1d485", - }} + offset={0.413} + style={{ + stopColor: "#f1d485", + }} /> <stop - offset={0.527} - style={{ - stopColor: "#f2d998", - }} + offset={0.527} + style={{ + stopColor: "#f2d998", + }} /> <stop - offset={0.607} - style={{ - stopColor: "#f3db9f", - }} + offset={0.607} + style={{ + stopColor: "#f3db9f", + }} /> <stop - offset={1} - style={{ - stopColor: "#e6b711", - }} + offset={1} + style={{ + stopColor: "#e6b711", + }} /> </linearGradient> <path - d="M139.2 193v-1.8c5-.4 10.2-.5 13.4-5 3.8-5.2 3.2-15.4 3.2-21.5V78.5l-47.3 120.4L61.2 81.2v61.1c0 13.6.5 35.1 12.5 44.2 4.3 3.2 9 3.9 14.2 4.7v1.8H31.1v-1.8c1.6-.4 3.2-.7 4.8-1.1 22.6-5.7 22.9-32.4 22.9-51.1v-37.8c0-6.1.5-13.3-3.2-18.5-4.5-6.1-13.4-7.2-20.4-7.5v-1.8H96l29.6 75.4 29.9-75.4h52.3v1.8c-4.8.4-10.2.9-13.4 5-3.8 4.8-3.2 15.9-3.2 21.9v62.5c0 6.3-.7 16.1 3.2 21.5 3.2 4.3 6.2 4.7 11.2 5v1.8h-66.4z" - style={{ - fill: "url(#d)", - }} + d="M139.2 193v-1.8c5-.4 10.2-.5 13.4-5 3.8-5.2 3.2-15.4 3.2-21.5V78.5l-47.3 120.4L61.2 81.2v61.1c0 13.6.5 35.1 12.5 44.2 4.3 3.2 9 3.9 14.2 4.7v1.8H31.1v-1.8c1.6-.4 3.2-.7 4.8-1.1 22.6-5.7 22.9-32.4 22.9-51.1v-37.8c0-6.1.5-13.3-3.2-18.5-4.5-6.1-13.4-7.2-20.4-7.5v-1.8H96l29.6 75.4 29.9-75.4h52.3v1.8c-4.8.4-10.2.9-13.4 5-3.8 4.8-3.2 15.9-3.2 21.9v62.5c0 6.3-.7 16.1 3.2 21.5 3.2 4.3 6.2 4.7 11.2 5v1.8h-66.4z" + style={{ + fill: "url(#d)", + }} /> <linearGradient - gradientUnits="userSpaceOnUse" - id="e" - x1={30.453} - x2={213.794} - y1={63.629} - y2={63.629} + gradientUnits="userSpaceOnUse" + id="e" + x1={30.453} + x2={213.794} + y1={63.629} + y2={63.629} > <stop - offset={0} - style={{ - stopColor: "#e6b711", - }} + offset={0} + style={{ + stopColor: "#e6b711", + }} /> <stop - offset={0.128} - style={{ - stopColor: "#eac23c", - }} + offset={0.128} + style={{ + stopColor: "#eac23c", + }} /> <stop - offset={0.278} - style={{ - stopColor: "#ec6", - }} + offset={0.278} + style={{ + stopColor: "#ec6", + }} /> <stop - offset={0.413} - style={{ - stopColor: "#f1d485", - }} + offset={0.413} + style={{ + stopColor: "#f1d485", + }} /> <stop - offset={0.527} - style={{ - stopColor: "#f2d998", - }} + offset={0.527} + style={{ + stopColor: "#f2d998", + }} /> <stop - offset={0.607} - style={{ - stopColor: "#f3db9f", - }} + offset={0.607} + style={{ + stopColor: "#f3db9f", + }} /> <stop - offset={1} - style={{ - stopColor: "#e6b711", - }} + offset={1} + style={{ + stopColor: "#e6b711", + }} /> </linearGradient> <path - d="M30.5 60.6h183.3v6.1H30.5z" - style={{ - fill: "url(#e)", - }} + d="M30.5 60.6h183.3v6.1H30.5z" + style={{ + fill: "url(#e)", + }} /> <linearGradient - gradientUnits="userSpaceOnUse" - id="f" - x1={196.676} - x2={225.538} - y1={132.923} - y2={132.923} + gradientUnits="userSpaceOnUse" + id="f" + x1={196.676} + x2={225.538} + y1={132.923} + y2={132.923} > <stop - offset={0} - style={{ - stopColor: "#e6b711", - }} + offset={0} + style={{ + stopColor: "#e6b711", + }} /> <stop - offset={0.128} - style={{ - stopColor: "#eac23c", - }} + offset={0.128} + style={{ + stopColor: "#eac23c", + }} /> <stop - offset={0.278} - style={{ - stopColor: "#ec6", - }} + offset={0.278} + style={{ + stopColor: "#ec6", + }} /> <stop - offset={0.413} - style={{ - stopColor: "#f1d485", - }} + offset={0.413} + style={{ + stopColor: "#f1d485", + }} /> <stop - offset={0.527} - style={{ - stopColor: "#f2d998", - }} + offset={0.527} + style={{ + stopColor: "#f2d998", + }} /> <stop - offset={0.607} - style={{ - stopColor: "#f3db9f", - }} + offset={0.607} + style={{ + stopColor: "#f3db9f", + }} /> <stop - offset={1} - style={{ - stopColor: "#e6b711", - }} + offset={1} + style={{ + stopColor: "#e6b711", + }} /> </linearGradient> <path - d="M196.7 177.5c11.5-15.6 18.7-35.2 18.7-56 0-14.1-3.1-27.5-8.8-39.5h11.1c5 12.2 7.8 25.5 7.8 39.5 0 23.4-7.8 45-20.9 62.4l-7.9-6.4z" - style={{ - fill: "url(#f)", - }} + d="M196.7 177.5c11.5-15.6 18.7-35.2 18.7-56 0-14.1-3.1-27.5-8.8-39.5h11.1c5 12.2 7.8 25.5 7.8 39.5 0 23.4-7.8 45-20.9 62.4l-7.9-6.4z" + style={{ + fill: "url(#f)", + }} /> </svg> ) diff --git a/playbook/app/pb_kits/playbook/pb_user_badge/badges/veteran.tsx b/playbook/app/pb_kits/playbook/pb_user_badge/badges/veteran.tsx index 25c92bdec2..f9da98862a 100644 --- a/playbook/app/pb_kits/playbook/pb_user_badge/badges/veteran.tsx +++ b/playbook/app/pb_kits/playbook/pb_user_badge/badges/veteran.tsx @@ -1,6 +1,6 @@ import * as React from "react" -const Veteran = () => ( +const Veteran = (): React.ReactElement => ( <svg viewBox="0 0 200 250" xmlSpace="preserve" diff --git a/playbook/app/pb_kits/playbook/pb_walkthrough/_walkthrough.tsx b/playbook/app/pb_kits/playbook/pb_walkthrough/_walkthrough.tsx index 795556fd18..f305a29cd3 100644 --- a/playbook/app/pb_kits/playbook/pb_walkthrough/_walkthrough.tsx +++ b/playbook/app/pb_kits/playbook/pb_walkthrough/_walkthrough.tsx @@ -1,3 +1,5 @@ +/* eslint-disable react/no-multi-comp */ + import React from 'react' import classnames from 'classnames' import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props' @@ -24,7 +26,7 @@ type WalkthroughProps = { disableOverlay?: boolean, disableOverlayClose?: boolean, disableScrolling?: boolean, - floaterProps?: object, + floaterProps?: Record<string, unknown>, hideBackButton?: boolean, hideCloseButton?: boolean, showProgress?: boolean, @@ -58,89 +60,92 @@ type TooltipProps = { disableBeacon?: boolean, }, skip?: boolean, - backProps?: object, - closeProps?: object, - primaryProps?: object, - skipProps?: object, - tooltipProps?: object, + backProps?: Record<string, unknown>, + closeProps?: Record<string, unknown>, + primaryProps?: Record<string, unknown>, + skipProps?: Record<string, unknown>, + tooltipProps?: Record<string, unknown>, } +// eslint-disable-next-line react/display-name const Tooltip = React.forwardRef((props: TooltipProps) => ( - <div +<div className="pb_card_kit_border_none p_none" {...props.tooltipProps} +> + {props.step.title && <div> + <Flex + align="center" + justify="between" + padding="xs" > - {props.step.title && <div> - <Flex - align="center" - justify="between" - padding="xs" - > - <Title + <Title paddingLeft="xs" size={4} - > - {props.step.title} - - {props.skip && (