Skip to content

Commit

Permalink
[PLAY-1581] Height, minHeight, maxHeight for Card, Flex, FlexItem, & …
Browse files Browse the repository at this point in the history
…Dialog (#3749)

**What does this PR do?** A clear and concise description with your
runway ticket url.
https://runway.powerhrg.com/backlog_items/PLAY-1581


In order to achieve the height functionality that the story demanded, we
introduced a new utility function for handling specific global
properties that require _dynamic_ values (any px, %, or vh, etc). This
has been on our Todo list for a looooong time, and I'm so happy we were
able to knock out the work here in this story!

The work in this PR adds the following props for react: **minHeight,
maxHeight, height** for React
The work in this PR adds the following props for rails: **min_height,
max_height, height** for Rails

For now, these are applied to a few of our utility kits: Card, Flex,
FlexItem, Dialog

We have a path forward to apply to all other kits, but for now this work
unblocks the Template work for the PB-In-Nitro team, without shipping
changes to every kit which would move this to a much higher risk-level.


**Screenshots:** Screenshots to visualize your addition/change


**How to test?** Steps to confirm the desired behavior:
1. Go to dialog, flex, flex_item, or card.
2. Add a height prop with a value. 
3. See your styles get applied.


#### Checklist:
- [X] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new
kit`, `deprecated`, or `breaking`. See [Changelog &
Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels)
for details.
- [X] **DEPLOY** I have added the `milano` label to show I'm ready for a
review.
- [X] **TESTS** I have added test coverage to my code.
  • Loading branch information
jasperfurniss authored Oct 22, 2024
1 parent f4c5e2f commit d7b8e5e
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 17 deletions.
6 changes: 5 additions & 1 deletion 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,7 @@ const Card = (props: CardPropTypes): React.ReactElement => {

// coerce to array
const cardChildren = React.Children.toArray(children)
const dynamicInlineProps = globalInlineProps(props);

const subComponentTags = (tagName: string) => {
return cardChildren.filter((c: string) => (
Expand Down Expand Up @@ -135,6 +137,7 @@ const Card = (props: CardPropTypes): React.ReactElement => {
{...dataProps}
{...htmlProps}
className={classnames(cardCss, globalProps(props), className)}
style={dynamicInlineProps}
>
{subComponentTags('Header')}
{
Expand Down Expand Up @@ -163,6 +166,7 @@ const Card = (props: CardPropTypes): React.ReactElement => {
{...dataProps}
{...htmlProps}
className={classnames(cardCss, globalProps(props), className)}
style={dynamicInlineProps}
>
{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 @@ -170,12 +170,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 & { 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 @@ -498,7 +510,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 @@ -510,6 +537,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
22 changes: 21 additions & 1 deletion playbook/lib/playbook/kit_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ 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
Expand All @@ -82,6 +86,14 @@ def combined_html_options
default_html_options.merge(html_options.deep_merge(data_attributes))
end

def global_inline_props
{
height: height,
min_height: min_height,
max_height: max_height,
}.compact
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 @@ -113,12 +125,15 @@ def pb_content_tag(name = :div, content_or_options_with_block = {}, options = {}
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?
options
end

def default_html_options
Expand All @@ -131,5 +146,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 d7b8e5e

Please sign in to comment.