Skip to content

Commit

Permalink
[PLAY-1627] Fix Height Prop for HtmlOptions Support (#3873)
Browse files Browse the repository at this point in the history
**What does this PR do?** A clear and concise description with your
runway ticket url.
https://runway.powerhrg.com/backlog_items/PLAY-1627

**Screenshots:** Screenshots to visualize your addition/change
The htmlOptions now works, even with the addition of our new height
prop.

**How to test?** Steps to confirm the desired behavior:
1. Go to a doc example for Card of Flex kit, and add a new height prop. 
2. Test one that also has our htmlOptions prop.


#### Checklist:
- [X] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new
kit`, `deprecated`, or `breaking`. See [Changelog &
Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels)
for details.
- [X] **DEPLOY** I have added the `milano` label to show I'm ready for a
review.
~- [ ] **TESTS** I have added test coverage to my code.~
  • Loading branch information
jasperfurniss authored Nov 8, 2024
1 parent aa29701 commit 3ee381a
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 24 deletions.
15 changes: 11 additions & 4 deletions playbook/app/pb_kits/playbook/pb_card/_card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { get } from 'lodash'
import classnames from 'classnames'

import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
import { GlobalProps, globalProps } from '../utilities/globalProps'
import { GlobalProps, globalProps, globalInlineProps } from '../utilities/globalProps'
import type { ProductColors, CategoryColors, BackgroundColors } from '../types/colors'

import Icon from '../pb_icon/_icon'
Expand Down Expand Up @@ -49,6 +49,7 @@ type CardBodyProps = {
padding?: string,
} & GlobalProps


// Header component
const Header = (props: CardHeaderProps) => {
const { children, className, headerColor = 'category_1', headerColorStriped = false } = props
Expand Down Expand Up @@ -107,6 +108,10 @@ const Card = (props: CardPropTypes): React.ReactElement => {

// coerce to array
const cardChildren = React.Children.toArray(children)
const dynamicInlineProps = globalInlineProps(props);
const { style: htmlStyle = {}, ...restHtmlProps } = htmlProps as { style?: React.CSSProperties };
const mergedStyles: React.CSSProperties = { ...htmlStyle, ...dynamicInlineProps };


const subComponentTags = (tagName: string) => {
return cardChildren.filter((c: string) => (
Expand All @@ -122,7 +127,7 @@ const Card = (props: CardPropTypes): React.ReactElement => {

const tagOptions = ['div', 'section', 'footer', 'header', 'article', 'aside', 'main', 'nav']
const Tag = tagOptions.includes(tag) ? tag : 'div'

return (
<>
{
Expand All @@ -133,8 +138,9 @@ const Card = (props: CardPropTypes): React.ReactElement => {
<Tag
{...ariaProps}
{...dataProps}
{...htmlProps}
className={classnames(cardCss, globalProps(props), className)}
{...restHtmlProps}
style={mergedStyles}
>
{subComponentTags('Header')}
{
Expand All @@ -161,8 +167,9 @@ const Card = (props: CardPropTypes): React.ReactElement => {
<Tag
{...ariaProps}
{...dataProps}
{...htmlProps}
className={classnames(cardCss, globalProps(props), className)}
{...restHtmlProps}
style={mergedStyles}
>
{subComponentTags('Header')}
{nonHeaderChildren}
Expand Down
6 changes: 5 additions & 1 deletion playbook/app/pb_kits/playbook/pb_dialog/_dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import classnames from "classnames";
import Modal from "react-modal";

import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props";
import { globalProps } from "../utilities/globalProps";
import { globalProps, globalInlineProps } from "../utilities/globalProps";

import Body from "../pb_body/_body";
import Button from "../pb_button/_button";
Expand Down Expand Up @@ -91,6 +91,8 @@ const Dialog = (props: DialogProps): React.ReactElement => {
beforeClose: "pb_dialog_overlay_before_close",
};

const dynamicInlineProps = globalInlineProps(props);

const classes = classnames(
buildCss("pb_dialog_wrapper"),
globalProps(props),
Expand Down Expand Up @@ -184,6 +186,7 @@ const Dialog = (props: DialogProps): React.ReactElement => {
overlayClassName={overlayClassNames}
portalClassName={portalClassName}
shouldCloseOnOverlayClick={shouldCloseOnOverlayClick && !loading}
style={{ content: dynamicInlineProps }}
>
<>
{title && !status ? <Dialog.Header>{title}</Dialog.Header> : null}
Expand All @@ -192,6 +195,7 @@ const Dialog = (props: DialogProps): React.ReactElement => {
<Dialog.Body
className="dialog_status_text_align"
padding="md"

>
<Flex align="center"
orientation="column"
Expand Down
4 changes: 3 additions & 1 deletion playbook/app/pb_kits/playbook/pb_flex/_flex.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import classnames from 'classnames'
import { buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
import { GlobalProps, globalProps } from '../utilities/globalProps'
import { GlobalProps, globalProps, globalInlineProps } from '../utilities/globalProps'
import { GenericObject, Sizes } from '../types'

type FlexProps = {
Expand Down Expand Up @@ -61,6 +61,7 @@ const Flex = (props: FlexProps): React.ReactElement => {
const alignSelfClass = alignSelf !== 'none' ? `align_self_${alignSelf}` : ''
const dataProps = buildDataProps(data)
const htmlProps = buildHtmlProps(htmlOptions)
const dynamicInlineProps = globalInlineProps(props)


return (
Expand All @@ -83,6 +84,7 @@ const Flex = (props: FlexProps): React.ReactElement => {
globalProps(props),
className
)}
style={dynamicInlineProps}
{...dataProps}
{...htmlProps}
>
Expand Down
10 changes: 8 additions & 2 deletions playbook/app/pb_kits/playbook/pb_flex/_flex_item.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import classnames from 'classnames'
import { buildCss, buildHtmlProps } from '../utilities/props'
import { globalProps, GlobalProps } from '../utilities/globalProps'
import { globalProps, GlobalProps, globalInlineProps} from '../utilities/globalProps'
type FlexItemPropTypes = {
children: React.ReactNode[] | React.ReactNode,
fixedSize?: string,
Expand Down Expand Up @@ -35,14 +35,20 @@ const FlexItem = (props: FlexItemPropTypes): React.ReactElement => {
const fixedStyle =
fixedSize !== undefined ? { flexBasis: `${fixedSize}` } : null
const orderClass = order !== 'none' ? `order_${order}` : null
const dynamicInlineProps = globalInlineProps(props)
const combinedStyles = {
...fixedStyle,
...dynamicInlineProps
}

const htmlProps = buildHtmlProps(htmlOptions)


return (
<div
{...htmlProps}
className={classnames(buildCss('pb_flex_item_kit', growClass, shrinkClass, flexClass, displayFlexClass), orderClass, alignSelfClass, globalProps(props), className)}
style={fixedStyle}
style={combinedStyles}
>
{children}
</div>
Expand Down
9 changes: 3 additions & 6 deletions playbook/app/pb_kits/playbook/pb_flex/flex_item.html.erb
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<%= content_tag(:div,
id: object.id,
data: object.data,
class: object.classname,
style: object.style_value,
**combined_html_options) do %>
<%= pb_content_tag(:div,
style: object.inline_styles
) do %>
<%= content.presence %>
<% end %>
9 changes: 7 additions & 2 deletions playbook/app/pb_kits/playbook/pb_flex/flex_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ def classname
generate_classname("pb_flex_item_kit", fixed_size_class, grow_class, shrink_class, display_flex_class) + align_self_class
end

def style_value
"flex-basis: #{fixed_size};" if fixed_size.present?
def inline_styles
styles = []
styles << "flex-basis: #{fixed_size};" if fixed_size.present?
styles << "height: #{height};" if height.present?
styles << "min-height: #{min_height};" if min_height.present?
styles << "max-height: #{max_height};" if max_height.present?
styles.join(" ")
end

private
Expand Down
2 changes: 1 addition & 1 deletion playbook/app/pb_kits/playbook/pb_popover/_popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import classnames from "classnames";
import { globalProps, GlobalProps } from "../utilities/globalProps";
import { uniqueId } from 'lodash';

type ModifiedGlobalProps = Omit<GlobalProps, 'minWidth'>
type ModifiedGlobalProps = Omit<GlobalProps, 'minWidth' | 'maxHeight' | 'minHeight'>

type PbPopoverProps = {
aria?: { [key: string]: string };
Expand Down
3 changes: 3 additions & 0 deletions playbook/app/pb_kits/playbook/utilities/globalPropNames.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export default [
"minHeight",
"maxHeight",
"height",
"left",
"bottom",
"right",
Expand Down
41 changes: 39 additions & 2 deletions playbook/app/pb_kits/playbook/utilities/globalProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,24 @@ type ZIndex = {
zIndex?: ZIndexType,
} | ZIndexResponsiveType

type Height = {
height?: string
}

type MaxHeight = {
maxHeight?: string
}

type MinHeight = {
minHeight?: string
}

// keep this as the last type definition
export type GlobalProps = AlignContent & AlignItems & AlignSelf &
BorderRadius & Cursor & Dark & Display & DisplaySizes & Flex & FlexDirection &
FlexGrow & FlexShrink & FlexWrap & JustifyContent & JustifySelf &
LineHeight & Margin & MinWidth & MaxWidth & NumberSpacing & Order & Overflow & Padding &
Position & Shadow & TextAlign & Truncate & VerticalAlign & ZIndex & GroupHover & { hover?: string } & Top & Right & Bottom & Left;
Position & Shadow & TextAlign & Truncate & VerticalAlign & ZIndex & { hover?: string } & Top & Right & Bottom & Left & Height & MaxHeight & MinHeight;

const getResponsivePropClasses = (prop: {[key: string]: string}, classPrefix: string) => {
const keys: string[] = Object.keys(prop)
Expand Down Expand Up @@ -503,7 +515,22 @@ const PROP_CATEGORIES: {[key:string]: (props: {[key: string]: any}) => string} =
} else {
return verticalAlign ? `vertical_align_${verticalAlign} ` : ''
}
}
},

}

const PROP_INLINE_CATEGORIES: {[key:string]: (props: {[key: string]: any}) => {[key: string]: any}} = {
heightProps: ({ height }: Height) => {
return height ? { height } : {};
},

maxHeightProps: ({ maxHeight }: MaxHeight) => {
return maxHeight ? { maxHeight } : {};
},

minHeightProps: ({ minHeight }: MinHeight) => {
return minHeight ? { minHeight } : {};
},
}

type DefaultProps = {[key: string]: string} | Record<string, unknown>
Expand All @@ -515,6 +542,16 @@ export const globalProps = (props: GlobalProps, defaultProps: DefaultProps = {})
}).filter((value) => value?.length > 0).join(" ")
}

// New function for inline styles
export const globalInlineProps = (props: GlobalProps): React.CSSProperties => {
const styles = Object.keys(PROP_INLINE_CATEGORIES).reduce((acc, key) => {
const result = PROP_INLINE_CATEGORIES[key](props);
return { ...acc, ...(typeof result === 'object' ? result : {}) }; // Ensure result is an object before spreading
}, {});

return styles; // Return the styles object directly
}


export const deprecatedProps = (): void => {
// if (process.env.NODE_ENV === 'development') {
Expand Down
48 changes: 43 additions & 5 deletions playbook/lib/playbook/kit_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ class KitBase < ViewComponent::Base
prop :aria, type: Playbook::Props::HashProp, default: {}
prop :html_options, type: Playbook::Props::HashProp, default: {}
prop :children, type: Playbook::Props::Proc
prop :style, type: Playbook::Props::HashProp, default: {}
prop :height
prop :min_height
prop :max_height

def object
self
end

def combined_html_options
default_html_options.merge(html_options.deep_merge(data_attributes))
end

# rubocop:disable Layout/CommentIndentation
# pb_content_tag information (potentially to be abstracted into its own dev doc in the future)
# The pb_content_tag generates HTML content tags for rails kits with flexible options.
Expand Down Expand Up @@ -110,15 +110,48 @@ def pb_content_tag(name = :div, content_or_options_with_block = {}, options = {}
end
# rubocop:enable Style/OptionalBooleanParameter

def combined_html_options
merged = default_html_options.dup

html_options.each do |key, value|
if key == :style && value.is_a?(Hash)
# Convert style hash to CSS string
merged[:style] = value.map { |k, v| "#{k.to_s.gsub('_', '-')}: #{v}" }.join("; ")
else
merged[key] = value
end
end

inline_styles = dynamic_inline_props
merged[:style] = if inline_styles.present?
merged[:style].present? ? "#{merged[:style]}; #{inline_styles}" : inline_styles
end

merged.deep_merge(data_attributes)
end

def global_inline_props
{
height: height,
min_height: min_height,
max_height: max_height,
}.compact
end

private

def default_options
{
options = {
id: id,
data: data,
class: classname,
aria: aria,
}

inline_styles = dynamic_inline_props
options[:style] = inline_styles if inline_styles.present? && !html_options.key?(:style)

options
end

def default_html_options
Expand All @@ -131,5 +164,10 @@ def data_attributes
aria: aria,
}.transform_keys { |key| key.to_s.tr("_", "-").to_sym }
end

def dynamic_inline_props
styles = global_inline_props.map { |key, value| "#{key.to_s.gsub('_', '-')}: #{value}" if value.present? }.compact
styles.join("; ").presence
end
end
end

0 comments on commit 3ee381a

Please sign in to comment.