diff --git a/UpgradeGuide.md b/UpgradeGuide.md
index bb185205c..16c3e25b5 100644
--- a/UpgradeGuide.md
+++ b/UpgradeGuide.md
@@ -9,6 +9,8 @@
- Check your sizes, standalone props have been replaced by the `size` prop, such as `size=medium` instead of `medium`.
- `Checkbox`
- The old `theme` prop functionality has been replaced by the [global theme object](https://faithlife.github.io/styled-ui/#/theme) and Styled System props.
+- `HelpBox`
+ - The old `theme` prop functionality has been replaced by the [global theme object](https://faithlife.github.io/styled-ui/#/theme) and Styled System props.
- `Modal`
- Subcomponents have been renamed, `ModalContent` has been replaced by `Modal.Content`, etc.
- `Radio`
diff --git a/catalog/help-box/documentation-v6.md b/catalog/help-box/documentation-v6.md
new file mode 100644
index 000000000..27f665319
--- /dev/null
+++ b/catalog/help-box/documentation-v6.md
@@ -0,0 +1,11 @@
+For the next major version of Styled UI, the HelpBox component has been rebuilt to use Styled System's theme and variant APIs.
+
+You can opt in to the new API now by importing `{ HelpBox } from '@faithlife/styled-ui/v6'`. When v6 is released, the `/v6` entrypoint will continue to be supported with a deprecation warning until v7 is released.
+
+This documentation is automatically generated from jsdoc comments.
+
+```react
+noSource: true
+---
+
+```
diff --git a/catalog/help-box/variations-v6.md b/catalog/help-box/variations-v6.md
new file mode 100644
index 000000000..24357bc2e
--- /dev/null
+++ b/catalog/help-box/variations-v6.md
@@ -0,0 +1,63 @@
+For the next major version of Styled UI, the HelpBox component has been rebuilt to use Styled System's theme and variant APIs.
+
+You can opt in to the new API now by importing `{ HelpBox } from '@faithlife/styled-ui/v6'`. When v6 is released, the `/v6` entrypoint will continue to be supported with a deprecation warning until v7 is released.
+
+## Colors and Their Meaning
+
+```react
+showSource: true
+---
+
+ true}>
+ This is a helpful alert.
+
+
+ true}>
+ This is an error alert.
+
+
+ true}>
+ This is a successful alert.
+
+
+ true}>
+ This is a cautious alert.
+
+
+ true}>
+ This is a minor alert.
+
+
+
+```
+
+## Variations
+
+```react
+showSource: true
+---
+
+ true}>This is an alert with a light bulb.
+ true}>This alert has its icon hidden.
+ This alert is showing its icon on both sides.
+ true}>
+ This alert's contents are stacked.
+
+
+
+ This alert doesn't handle closing.
+
+
+
+```
+
+## Large Alerts
+
+```react
+showSource: true
+---
+
+ true}>This is a large alert.
+ true}>This is a large alert with a light bulb.
+
+```
diff --git a/catalog/index.js b/catalog/index.js
index 7dfa3ed9c..5f9f22170 100644
--- a/catalog/index.js
+++ b/catalog/index.js
@@ -72,6 +72,7 @@ import {
SegmentedButtonGroup,
Checkbox as V6Checkbox,
Dropdown as V6Dropdown,
+ HelpBox as V6HelpBox,
Modal as V6Modal,
Radio as V6Radio,
SimpleToast as V6SimpleToast,
@@ -586,6 +587,22 @@ const pages = [
content: pageLoader(() => import('./help-box/documentation.md')),
imports: { HelpBox, DocgenTable },
},
+ {
+ path: '/help-box/variations-v6',
+ title: 'v6 Help Box Variations',
+ content: pageLoader(() => import('./help-box/variations-v6.md')),
+ imports: {
+ HelpBox: V6HelpBox,
+ Button: V6Button,
+ Stack,
+ },
+ },
+ {
+ path: '/help-box/documentation-v6',
+ title: 'v6 Help Box Documentation',
+ content: pageLoader(() => import('./help-box/documentation-v6.md')),
+ imports: { HelpBox: V6HelpBox, DocgenTable },
+ },
],
},
textInputPages,
diff --git a/components/help-box/component.jsx b/components/help-box/component.jsx
index eda47e49d..f9cdcf94e 100644
--- a/components/help-box/component.jsx
+++ b/components/help-box/component.jsx
@@ -6,8 +6,9 @@ import {
CorrectCircle as CircleCheck,
WarningCircle as Info,
} from '../icons/12px';
-import { applyVariations } from '../utils';
import * as Styled from './styled';
+import { getVariation } from '../utils';
+import { DefaultThemeProvider } from '../DefaultThemeProvider';
/** Rectangular box containing tips on how to use our products */
export function HelpBox({
@@ -16,65 +17,61 @@ export function HelpBox({
hideIcon,
showRightIcon,
stacked,
- className,
- theme,
handleClose,
+ variant,
+ primary,
+ success,
+ danger,
+ warning,
+ minor,
...helpBoxProps
}) {
- const { component: HelpBoxVariation, filteredProps } = applyVariations(
- Styled.HelpBox,
- Styled.variationMap,
- helpBoxProps,
- );
+ const selectedVariant =
+ getVariation(variant, { primary, success, danger, warning, minor }) ?? undefined;
return (
-
- {(showLightBulb && ) ||
- (!hideIcon && (
-
- {helpBoxProps.danger ? (
-
- ) : helpBoxProps.success ? (
-
- ) : helpBoxProps.minor ? null : (
-
- )}
-
- ))}
- {children}
- {(handleClose && (
-
-
-
- )) ||
- (showRightIcon && (
-
- {helpBoxProps.danger ? (
-
- ) : helpBoxProps.success ? (
-
- ) : helpBoxProps.minor ? null : (
-
- )}
-
- ))}
-
+
+
+ {(showLightBulb && ) ||
+ (!hideIcon && (
+
+ {selectedVariant === 'danger' ? (
+
+ ) : selectedVariant === 'success' ? (
+
+ ) : selectedVariant === 'minor' ? null : (
+
+ )}
+
+ ))}
+ {children}
+ {(handleClose && (
+
+
+
+ )) ||
+ (showRightIcon && (
+
+ {selectedVariant === 'danger' ? (
+
+ ) : selectedVariant === 'success' ? (
+
+ ) : selectedVariant === 'minor' ? null : (
+
+ )}
+
+ ))}
+
+
);
}
HelpBox.propTypes = {
- /** See the docs for how to override styles properly. */
- className: PropTypes.string,
children: PropTypes.node.isRequired,
/** The light bulb will override the other icon. */
showLightBulb: PropTypes.bool,
@@ -84,22 +81,19 @@ HelpBox.propTypes = {
showRightIcon: PropTypes.bool,
/** Stacking will happen automatically on small viewports. */
stacked: PropTypes.bool,
- /** Blue theme is the default.
- * The icons are colored by foregroundColor. */
- theme: PropTypes.shape({
- foregroundColor: PropTypes.string,
- backgroundColor: PropTypes.string,
- closeIconColor: PropTypes.string,
- }),
- /** Green theme */
+ /** Specifies the color variant (defaults to `primary`). */
+ variant: PropTypes.oneOf(['primary', 'success', 'danger', 'warning', 'minor']),
+ /** Shortcut for setting `variant` to `primary` (the blue theme). */
+ primary: PropTypes.bool,
+ /** Shortcut for setting `variant` to `success` (the green theme). */
success: PropTypes.bool,
- /** Red theme */
+ /** Shortcut for setting `variant` to `danger` (the red theme). */
danger: PropTypes.bool,
- /** Yellow theme */
+ /** Shortcut for setting `variant` to `warning` (the yellow theme). */
warning: PropTypes.bool,
- /** Gray theme */
+ /** Shortcut for setting `variant` to `minor` (the gray theme). */
minor: PropTypes.bool,
- /** Height will be 230px */
+ /** Sets height to `230px`. */
large: PropTypes.bool,
/** If not handled, there will be no close icon. */
handleClose: PropTypes.func,
diff --git a/components/help-box/index.js b/components/help-box/index.js
index b208c374b..6928009ca 100644
--- a/components/help-box/index.js
+++ b/components/help-box/index.js
@@ -1 +1,2 @@
+export { HelpBox as LegacyHelpBox } from './legacy-component';
export { HelpBox } from './component';
diff --git a/components/help-box/legacy-component.jsx b/components/help-box/legacy-component.jsx
new file mode 100644
index 000000000..cacc7d5f0
--- /dev/null
+++ b/components/help-box/legacy-component.jsx
@@ -0,0 +1,110 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ X as Close,
+ ErrorCircle as Exclamation,
+ CorrectCircle as CircleCheck,
+ WarningCircle as Info,
+} from '../icons/12px';
+import { applyVariations } from '../utils';
+import * as Styled from './legacy-styled';
+
+/** Rectangular box containing tips on how to use our products */
+export function HelpBox({
+ children,
+ showLightBulb,
+ hideIcon,
+ showRightIcon,
+ stacked,
+ className,
+ theme,
+ handleClose,
+ ...helpBoxProps
+}) {
+ const { component: HelpBoxVariation, filteredProps } = applyVariations(
+ Styled.HelpBox,
+ Styled.variationMap,
+ helpBoxProps,
+ );
+
+ return (
+
+ {(showLightBulb && ) ||
+ (!hideIcon && (
+
+ {helpBoxProps.danger ? (
+
+ ) : helpBoxProps.success ? (
+
+ ) : helpBoxProps.minor ? null : (
+
+ )}
+
+ ))}
+ {children}
+ {(handleClose && (
+
+
+
+ )) ||
+ (showRightIcon && (
+
+ {helpBoxProps.danger ? (
+
+ ) : helpBoxProps.success ? (
+
+ ) : helpBoxProps.minor ? null : (
+
+ )}
+
+ ))}
+
+ );
+}
+
+HelpBox.propTypes = {
+ /** See the docs for how to override styles properly. */
+ className: PropTypes.string,
+ children: PropTypes.node.isRequired,
+ /** The light bulb will override the other icon. */
+ showLightBulb: PropTypes.bool,
+ /** Hides the left icon. */
+ hideIcon: PropTypes.bool,
+ /** This icon will not show if closing is handled. */
+ showRightIcon: PropTypes.bool,
+ /** Stacking will happen automatically on small viewports. */
+ stacked: PropTypes.bool,
+ /** Blue theme is the default.
+ * The icons are colored by foregroundColor. */
+ theme: PropTypes.shape({
+ foregroundColor: PropTypes.string,
+ backgroundColor: PropTypes.string,
+ closeIconColor: PropTypes.string,
+ }),
+ /** Green theme */
+ success: PropTypes.bool,
+ /** Red theme */
+ danger: PropTypes.bool,
+ /** Yellow theme */
+ warning: PropTypes.bool,
+ /** Gray theme */
+ minor: PropTypes.bool,
+ /** Height will be 230px */
+ large: PropTypes.bool,
+ /** If not handled, there will be no close icon. */
+ handleClose: PropTypes.func,
+};
+
+HelpBox.Body = Styled.HelpBoxBody;
+
+HelpBox.Footer = Styled.HelpBoxFooter;
diff --git a/components/help-box/legacy-styled.jsx b/components/help-box/legacy-styled.jsx
new file mode 100644
index 000000000..05954a98c
--- /dev/null
+++ b/components/help-box/legacy-styled.jsx
@@ -0,0 +1,141 @@
+import styled from 'styled-components';
+import { fonts, colors, thickness } from '../../components/shared-styles';
+import { LightBulbH } from '../icons';
+import { resetStyles } from '../utils';
+import { mediaSizes } from '../shared-styles';
+
+export const HelpBoxContent = styled.div``;
+
+export const HelpBoxBody = styled.div``;
+
+export const HelpBoxFooter = styled.div``;
+
+export const BulbIcon = styled(LightBulbH)``;
+
+export const CloseButton = styled.button``;
+
+export const IconDiv = styled.div``;
+
+export const RightIconDiv = styled.div``;
+
+export const HelpBox = variantCreator(
+ colors.blueTint,
+ colors.blueLight,
+ colors.blueDark,
+)(styled.div`
+ position: relative;
+ display: flex;
+ border-radius: 3px;
+ word-break: break-word;
+
+ ${IconDiv} {
+ margin: ${({ hasIcon }) => (hasIcon ? '15px -4px 0px 16px' : '15px 4px 0px 0px')};
+
+ svg {
+ height: 18px;
+ width: 18px;
+ }
+ }
+
+ ${BulbIcon} {
+ flex: none;
+
+ width: ${props => (props.large ? '42px' : '24px')};
+ height: ${props => (props.large ? '42px' : '24px')};
+ margin: 12px 0px 0px 16px;
+ }
+
+ ${HelpBoxContent} {
+ ${fonts.c16};
+ display: flex;
+ flex: 1;
+ text-align: left;
+ line-height: 1.25;
+ font-size: 16px;
+ color: ${colors.flGray};
+
+ height: ${props => (props.large ? '230px' : '')};
+ padding: 14px 16px 14px 12px;
+
+ flex-direction: ${props => (props.stacked ? 'column' : 'row')};
+ @media (max-width: ${mediaSizes.phone}) {
+ flex-direction: column;
+ }
+
+ ${HelpBoxBody} {
+ ${fonts.c16};
+ display: flex;
+ flex: 1;
+ order: 2;
+ }
+
+ ${HelpBoxFooter} {
+ ${fonts.c16};
+ display: flex;
+ order: 2;
+ align-items: center;
+
+ margin: ${props => (props.stacked ? '12px 16px 0px 0px' : '-7px 0px -7px 16px')};
+ @media (max-width: ${mediaSizes.phone}) {
+ margin: 12px 0px 0px 0px;
+ }
+ }
+ }
+
+ ${CloseButton} {
+ cursor: pointer;
+ margin: ${props => (props.large ? '15px 16px 0px 16px' : '15px 16px 0px 12px')};
+ margin-left: ${props => (!props.stacked ? '-4px' : '')};
+ height: 18px;
+ background: transparent;
+ padding: 0;
+ border: none;
+
+ &::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+ }
+ }
+
+ ${RightIconDiv} {
+ margin: ${props => (props.large ? '15px 16px 0px 16px' : '15px 16px 0px 12px')};
+ margin-left: ${props => (!props.stacked ? '-4px' : '')};
+ height: 18px;
+ background: transparent;
+ padding: 0;
+ border: none;
+
+ &::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+ }
+ }
+`);
+
+export const variationMap = {
+ success: variantCreator(colors.greenTint, colors.greenLight, colors.greenDark),
+ danger: variantCreator(colors.redTint, colors.redLight, colors.redDark),
+ warning: variantCreator(colors.yellowTint, colors.yellowLight, colors.yellowDark),
+ minor: variantCreator(colors.gray4, colors.gray14, colors.gray34),
+};
+
+function variantCreator(backgroundColor, foregroundColor, closeIconColor) {
+ return component => styled(component)`
+ ${resetStyles};
+
+ background-color: ${props => props.theme.backgroundColor || backgroundColor};
+ border-left: solid ${thickness.four} ${props => props.theme.foregroundColor || foregroundColor};
+
+ ${IconDiv} {
+ path {
+ fill: ${props => props.theme.foregroundColor || foregroundColor};
+ }
+ }
+
+ ${CloseButton} {
+ path {
+ fill: ${props => props.theme.closeIconColor || closeIconColor};
+ }
+ }
+ `;
+}
diff --git a/components/help-box/styled.jsx b/components/help-box/styled.jsx
index 05954a98c..b880d12bc 100644
--- a/components/help-box/styled.jsx
+++ b/components/help-box/styled.jsx
@@ -1,8 +1,8 @@
import styled from 'styled-components';
-import { fonts, colors, thickness } from '../../components/shared-styles';
+import { variant } from 'styled-system';
+import { themeGet } from '@styled-system/theme-get';
import { LightBulbH } from '../icons';
import { resetStyles } from '../utils';
-import { mediaSizes } from '../shared-styles';
export const HelpBoxContent = styled.div``;
@@ -18,11 +18,7 @@ export const IconDiv = styled.div``;
export const RightIconDiv = styled.div``;
-export const HelpBox = variantCreator(
- colors.blueTint,
- colors.blueLight,
- colors.blueDark,
-)(styled.div`
+export const HelpBox = styled.div`
position: relative;
display: flex;
border-radius: 3px;
@@ -46,37 +42,41 @@ export const HelpBox = variantCreator(
}
${HelpBoxContent} {
- ${fonts.c16};
+ font-weight: ${themeGet('fontWeights.regular')};
display: flex;
flex: 1;
text-align: left;
line-height: 1.25;
font-size: 16px;
- color: ${colors.flGray};
+ color: ${themeGet('colors.flGray')};
height: ${props => (props.large ? '230px' : '')};
padding: 14px 16px 14px 12px;
flex-direction: ${props => (props.stacked ? 'column' : 'row')};
- @media (max-width: ${mediaSizes.phone}) {
+ @media (max-width: ${themeGet('breakpoints.phone')}) {
flex-direction: column;
}
${HelpBoxBody} {
- ${fonts.c16};
+ font-size: ${themeGet('fontSizes.3')};
+ font-weight: ${themeGet('fontWeights.regular')};
+ line-height: ${themeGet('lineHeights.3')};
display: flex;
flex: 1;
order: 2;
}
${HelpBoxFooter} {
- ${fonts.c16};
+ font-size: ${themeGet('fontSizes.3')};
+ font-weight: ${themeGet('fontWeights.regular')};
+ line-height: ${themeGet('lineHeights.3')};
display: flex;
order: 2;
align-items: center;
margin: ${props => (props.stacked ? '12px 16px 0px 0px' : '-7px 0px -7px 16px')};
- @media (max-width: ${mediaSizes.phone}) {
+ @media (max-width: ${themeGet('breakpoints.phone')}) {
margin: 12px 0px 0px 0px;
}
}
@@ -85,7 +85,7 @@ export const HelpBox = variantCreator(
${CloseButton} {
cursor: pointer;
margin: ${props => (props.large ? '15px 16px 0px 16px' : '15px 16px 0px 12px')};
- margin-left: ${props => (!props.stacked ? '-4px' : '')};
+ margin-left: ${props => !props.stacked && '-4px'};
height: 18px;
background: transparent;
padding: 0;
@@ -99,7 +99,7 @@ export const HelpBox = variantCreator(
${RightIconDiv} {
margin: ${props => (props.large ? '15px 16px 0px 16px' : '15px 16px 0px 12px')};
- margin-left: ${props => (!props.stacked ? '-4px' : '')};
+ margin-left: ${props => !props.stacked && '-4px'};
height: 18px;
background: transparent;
padding: 0;
@@ -110,32 +110,47 @@ export const HelpBox = variantCreator(
padding: 0;
}
}
-`);
-export const variationMap = {
- success: variantCreator(colors.greenTint, colors.greenLight, colors.greenDark),
- danger: variantCreator(colors.redTint, colors.redLight, colors.redDark),
- warning: variantCreator(colors.yellowTint, colors.yellowLight, colors.yellowDark),
- minor: variantCreator(colors.gray4, colors.gray14, colors.gray34),
+ ${resetStyles}
+
+ ${props =>
+ variant({
+ variants: {
+ primary: createColorVariant('primary', props),
+ success: createColorVariant('success', props),
+ danger: createColorVariant('danger', props),
+ warning: createColorVariant('warning', props),
+ minor: createColorVariant('minor', props),
+ },
+ })}
+`;
+
+HelpBox.defaultProps = {
+ variant: 'primary',
};
-function variantCreator(backgroundColor, foregroundColor, closeIconColor) {
- return component => styled(component)`
- ${resetStyles};
-
- background-color: ${props => props.theme.backgroundColor || backgroundColor};
- border-left: solid ${thickness.four} ${props => props.theme.foregroundColor || foregroundColor};
-
- ${IconDiv} {
- path {
- fill: ${props => props.theme.foregroundColor || foregroundColor};
- }
- }
-
- ${CloseButton} {
- path {
- fill: ${props => props.theme.closeIconColor || closeIconColor};
- }
- }
- `;
+/**
+ * Generates the styles for a given color variant.
+ *
+ * @param {'primary'|'success'|'danger'|'warning'|'minor'} name - The name of the color variant.
+ * @param {object} props - `HelpBox`'s `props` object.
+ * @returns {object} That variant's style object.
+ */
+function createColorVariant(name, props) {
+ return {
+ backgroundColor: `helpBox.${name}Background`,
+ borderLeft: `solid ${themeGet('space.2')(props)} ${themeGet(`colors.helpBox.${name}Foreground`)(
+ props,
+ )}`,
+ [IconDiv]: {
+ path: {
+ fill: themeGet(`colors.helpBox.${name}Foreground`)(props),
+ },
+ },
+ [CloseButton]: {
+ path: {
+ fill: themeGet(`colors.helpBox.${name}Icon`)(props),
+ },
+ },
+ };
}
diff --git a/components/utils/index.js b/components/utils/index.js
index 2ba5311db..b3887bfc5 100644
--- a/components/utils/index.js
+++ b/components/utils/index.js
@@ -5,11 +5,18 @@ export { filterChildProps } from './filter-props';
export { deprecate, deprecateComponent, deprecateProp } from './deprecate';
export { getConfigProps, getConfigChild } from './get-config-props';
-export function getVariation(variant, obj) {
+/**
+ * Chooses the correct variant from a main variant prop and a set of boolean shortcut props.
+ *
+ * @param {string|undefined} variant - The value of the component's `variant` prop.
+ * @param {{ [propName: string]: boolean }} shortcutProps - An object of shortcut prop names and boolean values, the names corresponding to variant name options.
+ * @returns {string|null} The name of the selected variant (or `null` if no variant has been selected).
+ */
+export function getVariation(variant, shortcutProps) {
if (variant) {
return variant;
}
- const match = [...Object.entries(obj)].find(entry => entry[1]);
+ const match = [...Object.entries(shortcutProps)].find(entry => entry[1]);
return match ? match[0] : null;
}
diff --git a/index-v6.js b/index-v6.js
index 3aa466382..f1b651ce9 100644
--- a/index-v6.js
+++ b/index-v6.js
@@ -1,6 +1,7 @@
export { Button, SegmentedButtonGroup } from './components/button';
export { Checkbox } from './components/check-box';
export { Dropdown } from './components/dropdown';
+export { HelpBox } from './components/help-box';
export { Modal } from './components/modal';
export { usePopover, Popover } from './components/popover-v6';
export { Radio } from './components/radio';
diff --git a/index.js b/index.js
index 654d06e19..ab0e5bc31 100644
--- a/index.js
+++ b/index.js
@@ -14,7 +14,7 @@ export { DatePickerInput } from './components/date-picker-input';
export { DatePeriodPicker } from './components/date-period-picker';
export { DropZone } from './components/drop-zone';
export { FilesSection } from './components/files-section';
-export { HelpBox } from './components/help-box';
+export { LegacyHelpBox as HelpBox } from './components/help-box';
export { Input, FilterInput, NumberInput } from './components/input';
export { LoadingSpinner } from './components/loading-spinner';
export {
diff --git a/theme/core.js b/theme/core.js
index 543468029..8093476a2 100644
--- a/theme/core.js
+++ b/theme/core.js
@@ -205,6 +205,24 @@ colors.datePickerInput = {
iconColor: colors.gray52,
};
+colors.helpBox = {
+ primaryBackground: colors.blue1,
+ primaryForeground: colors.blue3,
+ primaryIcon: colors.blue5,
+ successBackground: colors.green1,
+ successForeground: colors.green2,
+ successIcon: colors.green5,
+ dangerBackground: colors.red1,
+ dangerForeground: colors.red3,
+ dangerIcon: colors.red5,
+ warningBackground: colors.yellow1,
+ warningForeground: colors.yellow3,
+ warningIcon: colors.yellow5,
+ minorBackground: colors.gray4,
+ minorForeground: colors.gray14,
+ minorIcon: colors.gray34,
+};
+
colors.radio = {
primary: colors.blue4,
border: colors.checkbox.border,