diff --git a/UpgradeGuide.md b/UpgradeGuide.md
index f2aaf953d..361722a07 100644
--- a/UpgradeGuide.md
+++ b/UpgradeGuide.md
@@ -11,6 +11,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.
+- `DatePicker`
+ - `DatePicker` is now wrapped in a single `div` in the DOM, instead of being three separate DOM elements "wrapped" in a `React.Fragment`.
- `DatePickerInput`
- The `styleOverrides` prop has been removed in favor of Styled System props. Use style props for the input directly on the `DatePickerInput` component, and use style props for the calendar popover on a `DatePickerInput.Popover` child config component.
- The `placement` prop has been removed—use `placement` on `DatePickerInput.Popover` instead.
diff --git a/components/Paragraph.js b/components/Paragraph.js
index 32e83ceaf..ed2a28853 100644
--- a/components/Paragraph.js
+++ b/components/Paragraph.js
@@ -9,11 +9,11 @@ export const Paragraph = styled.p`
display: block;
margin: 0;
color: ${themeGet('colors.foregroundPrimary')};
- ${themeGet('textStyles.c.16')};
+ ${themeGet('textStyles.c.16')}
- ${textStyle};
- ${box};
- ${typography};
+ ${textStyle}
+ ${box}
+ ${typography}
`;
Paragraph.defaultProps = { theme };
diff --git a/components/Text.js b/components/Text.js
index a11ca081f..8d237aaaa 100644
--- a/components/Text.js
+++ b/components/Text.js
@@ -1,4 +1,3 @@
-import PropTypes from 'prop-types';
import systemPropTypes from '@styled-system/prop-types';
import styled from 'styled-components';
import { textStyle, layout, border } from 'styled-system';
@@ -10,20 +9,20 @@ export const Text = styled.span`
display: inline-flex;
align-items: baseline;
color: ${themeGet('colors.foregroundPrimary')};
- ${themeGet('textStyles.c.16')};
+ ${themeGet('textStyles.c.16')}
- ${textStyle};
- ${common};
- ${layout};
- ${typography};
- ${border};
+ ${textStyle}
+ ${common}
+ ${layout}
+ ${typography}
+ ${border}
`;
Text.defaultProps = { theme };
Text.propTypes = {
...common.propTypes,
...typography.propTypes,
+ ...systemPropTypes.textStyle,
...systemPropTypes.layout,
...systemPropTypes.border,
- textStyle: PropTypes.string,
};
diff --git a/components/accordion/accordion-item.jsx b/components/accordion/accordion-item.jsx
index 7135ea1b4..fab3e199c 100644
--- a/components/accordion/accordion-item.jsx
+++ b/components/accordion/accordion-item.jsx
@@ -4,7 +4,7 @@ import { useId } from '../shared-hooks';
import { Box } from '../Box';
import { useAccordionContext, AccordionItemContextProvider } from './accordion-util';
-export function AccordionItem({ children, index, pinned: isPinned }) {
+export function AccordionItem({ children, index, pinned: isPinned, ...otherProps }) {
const { expandedSections, onExpansion } = useAccordionContext();
const isExpanded = expandedSections.includes(index);
@@ -35,6 +35,7 @@ export function AccordionItem({ children, index, pinned: isPinned }) {
'header'
'panel'
`}
+ {...otherProps}
>
{children}
@@ -46,4 +47,7 @@ AccordionItem.propTypes = {
children: PropTypes.node.isRequired,
/** This is supplied by the Accordion component. */
index: PropTypes.number,
+ /** If `true`, the item will remain permanenty expanded. */
+ pinned: PropTypes.bool,
+ ...Box.propTypes,
};
diff --git a/components/button/Button.js b/components/button/Button.js
index 8bd92a481..0699e2584 100644
--- a/components/button/Button.js
+++ b/components/button/Button.js
@@ -1,12 +1,15 @@
import React from 'react';
+import PropTypes from 'prop-types';
import styled, { css } from 'styled-components';
import { variant, layout, flexbox, position, textStyle, border, background } from 'styled-system';
+import styledSystemPropTypes from '@styled-system/prop-types';
import 'focus-visible';
import { Box } from '../Box';
import { common, typography } from '../../theme/system';
import { buttonSizes, buttons } from '../../theme/buttons';
import { theme } from '../../theme';
import { LoadingSpinner } from '../loading-spinner';
+import { elementOfType } from '../utils';
const sizeVariant = variant({
prop: 'size',
@@ -113,6 +116,33 @@ const Button = React.forwardRef(
),
);
+Button.propTypes = {
+ children: PropTypes.node,
+ icon: PropTypes.element,
+ disabled: PropTypes.bool,
+ loading: PropTypes.bool,
+ active: PropTypes.bool,
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
+ variant: PropTypes.oneOf([
+ 'primary',
+ 'secondary',
+ 'minor',
+ 'transparent',
+ 'minorTransparent',
+ 'link',
+ 'danger',
+ 'dangerSpecial',
+ ]),
+ ...common.propTypes,
+ ...typography.propTypes,
+ ...styledSystemPropTypes.textStyle,
+ ...styledSystemPropTypes.layout,
+ ...styledSystemPropTypes.flexbox,
+ ...styledSystemPropTypes.position,
+ ...styledSystemPropTypes.border,
+ ...styledSystemPropTypes.background,
+};
+
const SegmentedButtonGroup = styled(Box).attrs(({ border }) => ({
border: border ?? 1,
borderColor: 'button.segmentedButtonGroupBorder',
@@ -143,4 +173,9 @@ const SegmentedButtonGroup = styled(Box).attrs(({ border }) => ({
}
`;
+SegmentedButtonGroup.propTypes = {
+ children: PropTypes.arrayOf(elementOfType(Button)).isRequired,
+ ...Box.propTypes,
+};
+
export { Button, SegmentedButtonGroup };
diff --git a/components/check-box/checkbox-content.jsx b/components/check-box/checkbox-content.jsx
index 96f11c868..aec118a51 100644
--- a/components/check-box/checkbox-content.jsx
+++ b/components/check-box/checkbox-content.jsx
@@ -4,6 +4,7 @@ import styledSystemPropTypes from '@styled-system/prop-types';
import styled from 'styled-components';
import { getConfigChild } from '../utils';
import { Text } from '../Text';
+import { common } from '../../theme/system';
import * as Styled from './styled';
export function CheckboxContent({ isChecked, title, disabled, children, ...otherProps }) {
@@ -30,8 +31,8 @@ CheckboxContent.propTypes = {
title: PropTypes.string,
isChecked: PropTypes.oneOf([true, false, 'mixed']),
disabled: PropTypes.bool,
+ ...common.propTypes,
...styledSystemPropTypes.position,
- ...styledSystemPropTypes.space,
...styledSystemPropTypes.layout,
};
@@ -41,8 +42,8 @@ CheckboxContent.propTypes = {
*/
export const CheckboxBox = props => null;
CheckboxBox.propTypes = {
+ ...common.propTypes,
...styledSystemPropTypes.position,
- ...styledSystemPropTypes.space,
...styledSystemPropTypes.layout,
};
CheckboxBox.childConfigComponent = 'CheckboxBox';
diff --git a/components/check-box/component.jsx b/components/check-box/component.jsx
index d4f47d86b..b0bb62bca 100644
--- a/components/check-box/component.jsx
+++ b/components/check-box/component.jsx
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { CheckboxContent } from './checkbox-content';
import * as Styled from './styled';
import { DefaultThemeProvider } from '../DefaultThemeProvider';
+import { common } from '../../theme/system';
/** Styled checkbox control with consistent styling across platforms */
export const Checkbox = function Checkbox({
@@ -67,6 +68,7 @@ Checkbox.propTypes = {
/** Disables automatic blur. */
disableAutoBlur: PropTypes.bool,
disabled: PropTypes.bool,
+ ...common.propTypes,
};
Checkbox.defaultProps = {
diff --git a/components/check-box/styled.jsx b/components/check-box/styled.jsx
index 42edf3de7..355fd8c32 100644
--- a/components/check-box/styled.jsx
+++ b/components/check-box/styled.jsx
@@ -1,5 +1,6 @@
import styled, { css } from 'styled-components';
-import { position, space, layout } from 'styled-system';
+import { position, layout } from 'styled-system';
+import { common } from '../../theme/system';
import { resetStyles } from '../utils';
import { Box } from '../Box';
@@ -19,8 +20,8 @@ export const CheckboxDiv = styled(Box)`
background-color: ${theme.colors.checkbox.disabledBackground};
`}
+ ${common}
${position}
- ${space}
${layout}
`;
@@ -64,6 +65,8 @@ export const CheckboxContainer = styled.button`
box-shadow: 0 0 0 2px ${({ theme }) => theme.colors.checkbox.shadowFocused};
}
}
+
+ ${common}
`;
export const isCheckedStyles = css`
diff --git a/components/collapse/component.jsx b/components/collapse/component.jsx
index c5b0e4d22..b93d29e14 100644
--- a/components/collapse/component.jsx
+++ b/components/collapse/component.jsx
@@ -8,6 +8,7 @@ const propTypes = {
...Transition.propTypes,
isOpen: PropTypes.bool,
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
+ ...Box.propTypes,
};
const defaultProps = {
diff --git a/components/date-period-picker/component.jsx b/components/date-period-picker/component.jsx
index 996c52540..d57249c66 100644
--- a/components/date-period-picker/component.jsx
+++ b/components/date-period-picker/component.jsx
@@ -4,6 +4,9 @@ import debounce from 'lodash.debounce';
import { DatePicker } from '../date-picker';
import { Input } from '../input';
import { dateFunctionProps } from '../date-picker/date-function-props';
+import { filterProps } from '../utils';
+import { common } from '../../theme/system';
+import { DefaultThemeProvider } from '../DefaultThemeProvider';
import * as Styled from './styled';
const DATE_FORMAT_STRING = 'M/d/yyyy';
@@ -40,6 +43,7 @@ export class DatePeriodPicker extends PureComponent {
dateFunctions: dateFunctionProps,
/** Debounce value for date inputs. Defaults to 500ms */
debounce: PropTypes.number,
+ ...common.propTypes,
};
static defaultProps = {
@@ -181,44 +185,45 @@ export class DatePeriodPicker extends PureComponent {
render() {
const { setSelectedDate, validate, dateFunctions } = this.props;
+ const { matchingProps: styleProps } = filterProps(this.props, common.propTypes);
const {
inputValues: { start, end },
selectedDateRange,
} = this.state;
return (
-
- {this.getUniqueDatePeriods().map(({ displayName, dateRange, originalIndex }) => (
- {
- setSelectedDate(dateRange, originalIndex);
- }}
- >
- {displayName}
-
- ))}
-
-
- From
- this.handleInputValueChange(event.target.value, 'start')}
- small
- />
-
-
- To
- this.handleInputValueChange(event.target.value, 'end')}
- small
- />
-
-
-
+
+
+ {this.getUniqueDatePeriods().map(({ displayName, dateRange, originalIndex }) => (
+ {
+ setSelectedDate(dateRange, originalIndex);
+ }}
+ >
+ {displayName}
+
+ ))}
+
+
+ From
+ this.handleInputValueChange(event.target.value, 'start')}
+ small
+ />
+
+
+ To
+ this.handleInputValueChange(event.target.value, 'end')}
+ small
+ />
+
+
-
-
+
+
);
}
}
diff --git a/components/date-period-picker/styled.jsx b/components/date-period-picker/styled.jsx
index 199a298c0..bd5be6714 100644
--- a/components/date-period-picker/styled.jsx
+++ b/components/date-period-picker/styled.jsx
@@ -1,5 +1,6 @@
import styled from 'styled-components';
-import { colors, thickness, fonts } from '../shared-styles';
+import { themeGet } from '@styled-system/theme-get';
+import { common } from '../../theme/system';
export const Container = styled.div`
display: flex;
@@ -12,25 +13,25 @@ export const Container = styled.div`
max-width: 324px;
}
- padding: ${thickness.four} 0 ${thickness.twelve} 0;
+ padding: ${themeGet('space.2')} 0 ${themeGet('space.4')} 0;
overflow: hidden;
-`;
-export const DatePickerContainer = styled.div`
- padding: ${thickness.eight};
+ ${common}
`;
export const DatePeriod = styled.div`
- font: ${fonts.ui14};
- padding: ${thickness.eight};
- background: ${colors.white};
+ ${themeGet('textStyles.ui.14')}
+
+ padding: ${themeGet('space.3')};
+ background: ${themeGet('colors.datePeriodPicker.background')};
cursor: pointer;
width: 100%;
+ box-sizing: border-box;
text-align: initial;
&:hover {
- background: ${colors.blueBase};
- color: ${colors.white};
+ background: ${themeGet('colors.datePeriodPicker.hoverBackground')};
+ color: ${themeGet('colors.datePeriodPicker.hoverText')};
transition: background 0.2s ease-out, color 0.2s ease-out;
}
`;
@@ -38,8 +39,8 @@ export const DatePeriod = styled.div`
export const DateInputContainer = styled.div`
display: flex;
flex-direction: row;
- border-top: 1px solid ${colors.gray14};
- padding: ${thickness.twelve} ${thickness.eight};
+ border-top: 1px solid ${themeGet('colors.datePeriodPicker.inputBorder')};
+ padding: ${themeGet('space.4')} ${themeGet('space.3')};
`;
export const Label = styled.label`
@@ -47,13 +48,14 @@ export const Label = styled.label`
text-align: initial;
display: flex;
flex-direction: column;
- font: ${fonts.ui14};
+
+ ${themeGet('textStyles.ui.14')}
&:first-of-type {
- padding-right: ${thickness.eight};
+ padding-right: ${themeGet('space.3')};
}
`;
export const LabelText = styled.span`
- padding-bottom: ${thickness.four};
+ padding-bottom: ${themeGet('space.2')};
`;
diff --git a/components/date-picker-input/component.jsx b/components/date-picker-input/component.jsx
index 8421ae327..ccb10bb4c 100644
--- a/components/date-picker-input/component.jsx
+++ b/components/date-picker-input/component.jsx
@@ -5,7 +5,7 @@ import { Popover } from '../popover-v6';
import { Calendar as CalendarIcon } from '../icons';
import { Input } from '../input';
import { dateFunctionProps } from '../date-picker/date-function-props';
-import { DatePicker } from '../date-picker/component';
+import { DatePicker } from '../date-picker';
import { UtilityButton } from '../button';
import * as Styled from './styled';
import { DefaultThemeProvider } from '../DefaultThemeProvider';
@@ -139,16 +139,15 @@ export function DatePickerInput({
zIndex={3}
{...popoverProps}
>
-
-
-
+
)}
diff --git a/components/date-picker-input/legacy-component.jsx b/components/date-picker-input/legacy-component.jsx
index 63d0bb41b..03833a86b 100644
--- a/components/date-picker-input/legacy-component.jsx
+++ b/components/date-picker-input/legacy-component.jsx
@@ -6,7 +6,7 @@ import { PlacementOptionsProps } from '../popover/popper-helpers';
import { Calendar as CalendarIcon } from '../icons';
import { Input } from '../input';
import { dateFunctionProps } from '../date-picker/date-function-props';
-import { DatePicker } from '../date-picker/component';
+import { DatePicker } from '../date-picker';
import * as Styled from './styled';
/** Flexible date picker input (with support for many date parsing libraries) */
@@ -145,16 +145,15 @@ export function DatePickerInput({
}}
{...popoverProps}
>
-
-
-
+
)}
diff --git a/components/date-picker-input/styled.jsx b/components/date-picker-input/styled.jsx
index b32e26837..de3b4a0a7 100644
--- a/components/date-picker-input/styled.jsx
+++ b/components/date-picker-input/styled.jsx
@@ -26,7 +26,3 @@ export const CalendarButton = styled(UtilityButton).attrs(({ theme, color }) =>
export const CalendarIconContainer = styled.div`
height: 18px; /* matches CalendarIcon */
`;
-
-export const DateTime = styled.div`
- width: 204px;
-`;
diff --git a/components/date-picker/component.jsx b/components/date-picker/component.jsx
index 5f9ecaeba..db5f1a9fe 100644
--- a/components/date-picker/component.jsx
+++ b/components/date-picker/component.jsx
@@ -1,8 +1,12 @@
-import React, { Component, Fragment } from 'react';
+import React, { Component } from 'react';
import PropTypes from 'prop-types';
+import styledSystemPropTypes from '@styled-system/prop-types';
import { Caret } from '../icons';
-import { colors } from '../shared-styles';
+import { theme } from '../../theme';
+import { DefaultThemeProvider } from '../DefaultThemeProvider';
import { dateFunctionProps } from './date-function-props';
+import { common } from '../../theme/system';
+import { filterProps } from '../utils';
import * as Styled from './styled';
import { CalendarWeek } from './calendar-week';
@@ -55,6 +59,8 @@ export class DatePicker extends Component {
dateFunctions: dateFunctionProps,
minDate: PropTypes.instanceOf(Date),
maxDate: PropTypes.instanceOf(Date),
+ ...common.propTypes,
+ ...styledSystemPropTypes.layout,
};
UNSAFE_componentWillMount() {
@@ -123,66 +129,72 @@ export class DatePicker extends Component {
minDate,
maxDate,
} = this.props;
+ const { matchingProps: styleProps } = filterProps(this.props, {
+ ...common.propTypes,
+ ...styledSystemPropTypes.layout,
+ });
const { currentMonth, weeks } = this.state;
return (
-
-
-
-
-
-
- {dateFunctions.format(currentMonth, 'MMMM yyyy')}
-
-
-
-
-
- S
- M
- T
- W
- T
- F
- S
-
-
- {weeks.map(week => (
-
- ))}
-
-
+
+
+
+
+
+
+
+ {dateFunctions.format(currentMonth, 'MMMM yyyy')}
+
+
+
+
+
+ S
+ M
+ T
+ W
+ T
+ F
+ S
+
+
+ {weeks.map(week => (
+
+ ))}
+
+
+
);
}
}
diff --git a/components/date-picker/index.js b/components/date-picker/index.js
index 5012bff02..3d959c10f 100644
--- a/components/date-picker/index.js
+++ b/components/date-picker/index.js
@@ -1 +1,4 @@
export { DatePicker } from './component';
+
+/** @todo Remove legacy export upon v6 release. */
+export { DatePicker as LegacyDatePicker } from './legacy-component';
diff --git a/components/date-picker/legacy-component.jsx b/components/date-picker/legacy-component.jsx
new file mode 100644
index 000000000..74f74dec3
--- /dev/null
+++ b/components/date-picker/legacy-component.jsx
@@ -0,0 +1,188 @@
+import React, { Component, Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { Caret } from '../icons';
+import { colors } from '../shared-styles';
+import { dateFunctionProps } from './date-function-props';
+import * as Styled from './legacy-styled';
+import { CalendarWeek } from './calendar-week';
+
+function generateWeek(sunday, { getYear, getMonth, getDate }) {
+ const week = [];
+ const year = getYear(sunday);
+ const month = getMonth(sunday);
+ const date = getDate(sunday);
+
+ for (let index = 0; index < 7; index++) {
+ week.push(new Date(year, month, date + index));
+ }
+
+ return week;
+}
+
+function generateWeeks(month, dateFunctions) {
+ const { startOfWeek, startOfMonth, endOfWeek, endOfMonth, isBefore, addWeeks } = dateFunctions;
+ const firstDay = startOfWeek(startOfMonth(month));
+ const lastDay = endOfWeek(endOfMonth(month));
+ const weeks = [];
+
+ for (
+ let walker = new Date(firstDay.getTime());
+ isBefore(walker, lastDay);
+ walker = addWeeks(walker, 1)
+ ) {
+ weeks.push(generateWeek(walker, dateFunctions));
+ }
+
+ return weeks;
+}
+
+/** Standard date picker control (with support for many different date parsing libraries) */
+export class DatePicker extends Component {
+ static propTypes = {
+ /** Sets the selected date */
+ selectedDate: PropTypes.instanceOf(Date),
+ /** Sets the selected date range (use with asDateRange prop) */
+ selectedDateRange: PropTypes.shape({
+ start: PropTypes.instanceOf(Date),
+ end: PropTypes.instanceOf(Date),
+ }),
+ /** A callback that retrieves the currently selected date or date range whenever the the selected dates change. */
+ setSelectedDate: PropTypes.func.isRequired,
+ /** Specifies that the component should function as a date range picker */
+ asDateRangePicker: PropTypes.bool,
+ /** Takes a date as a parameter and returns false if that date is invalid */
+ validate: PropTypes.func,
+ dateFunctions: dateFunctionProps,
+ minDate: PropTypes.instanceOf(Date),
+ maxDate: PropTypes.instanceOf(Date),
+ };
+
+ UNSAFE_componentWillMount() {
+ const selectedDate = this.props.selectedDate;
+ const date = selectedDate ? this.props.selectedDate : new Date();
+ this.setMonth(date);
+ }
+
+ setMonth = currentMonth => {
+ this.setState({
+ currentMonth,
+ weeks: generateWeeks(currentMonth, this.props.dateFunctions),
+ });
+ };
+
+ setSelectedDate = date => {
+ const { dateFunctions, selectedDateRange, asDateRangePicker, setSelectedDate } = this.props;
+ if (asDateRangePicker) {
+ let newDateRange;
+ if (selectedDateRange && selectedDateRange.start && !selectedDateRange.end) {
+ if (dateFunctions.isBefore(selectedDateRange.start, date)) {
+ newDateRange = { start: selectedDateRange.start, end: date };
+ } else {
+ newDateRange = { start: date, end: selectedDateRange.start };
+ }
+ } else {
+ newDateRange = { start: date };
+ }
+ setSelectedDate(newDateRange);
+ } else {
+ setSelectedDate(date);
+ }
+ };
+
+ decrementMonth = () => {
+ if (!this.canDecrementMonth(this.state.weeks)) {
+ return;
+ }
+ this.setMonth(this.props.dateFunctions.subMonths(this.state.currentMonth, 1));
+ };
+
+ incrementMonth = () => {
+ if (!this.canIncrementMonth(this.state.weeks)) {
+ return;
+ }
+ this.setMonth(this.props.dateFunctions.addMonths(this.state.currentMonth, 1));
+ };
+
+ canDecrementMonth = weeks => {
+ const firstDay = weeks[0][0];
+ return !this.props.minDate || this.props.minDate < firstDay;
+ };
+
+ canIncrementMonth = weeks => {
+ const lastDay = weeks[weeks.length - 1][weeks[weeks.length - 1].length - 1];
+ return !this.props.maxDate || this.props.maxDate > lastDay;
+ };
+
+ render() {
+ const {
+ selectedDate,
+ dateFunctions,
+ selectedDateRange,
+ asDateRangePicker,
+ validate,
+ minDate,
+ maxDate,
+ } = this.props;
+ const { currentMonth, weeks } = this.state;
+
+ return (
+
+
+
+
+
+
+ {dateFunctions.format(currentMonth, 'MMMM yyyy')}
+
+
+
+
+
+ S
+ M
+ T
+ W
+ T
+ F
+ S
+
+
+ {weeks.map(week => (
+
+ ))}
+
+
+ );
+ }
+}
diff --git a/components/date-picker/legacy-styled.jsx b/components/date-picker/legacy-styled.jsx
new file mode 100644
index 000000000..c470e8238
--- /dev/null
+++ b/components/date-picker/legacy-styled.jsx
@@ -0,0 +1,61 @@
+import styled from 'styled-components';
+import { colors } from '../shared-styles';
+
+export const ChangeMonth = styled.button`
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ border: none;
+ margin: 0;
+ padding: 0;
+ height: 34px;
+ width: 22px;
+ background: none;
+ cursor: pointer;
+
+ &:focus {
+ outline: ${({ visuallyDisabled }) => visuallyDisabled && `${colors.gray22} auto 1px`};
+ }
+`;
+
+export const Header = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ white-space: nowrap;
+ border-bottom: none;
+ background: ${colors.white};
+ color: ${colors.gray66};
+ line-height: 32px;
+ font-weight: bold;
+`;
+
+export const MonthLabel = styled.div`
+ display: inline-block;
+`;
+
+export const Week = styled.ul`
+ display: flex;
+ border-bottom: 1px solid ${colors.gray14};
+ padding: 8px 0;
+ background: ${colors.white};
+ list-style: none;
+ margin: 0;
+ color: ${colors.gray22};
+ font-size: 12px;
+`;
+
+export const WeekDay = styled.li`
+ flex: 1;
+ text-align: center;
+ text-transform: uppercase;
+`;
+
+export const Month = styled.div`
+ background: ${colors.white};
+ font-size: 14px;
+
+ @media (hover: none) {
+ max-width: 308px;
+ }
+`;
diff --git a/components/date-picker/styled.jsx b/components/date-picker/styled.jsx
index c470e8238..be6843fbf 100644
--- a/components/date-picker/styled.jsx
+++ b/components/date-picker/styled.jsx
@@ -1,5 +1,9 @@
import styled from 'styled-components';
-import { colors } from '../shared-styles';
+import { layout } from 'styled-system';
+import { themeGet } from '@styled-system/theme-get';
+import { common } from '../../theme/system';
+
+export const Container = styled.div(common, layout);
export const ChangeMonth = styled.button`
display: flex;
@@ -14,7 +18,8 @@ export const ChangeMonth = styled.button`
cursor: pointer;
&:focus {
- outline: ${({ visuallyDisabled }) => visuallyDisabled && `${colors.gray22} auto 1px`};
+ outline: ${({ visuallyDisabled }) =>
+ visuallyDisabled && `${themeGet('colors.datePicker.disabledOutline')} auto 1px`};
}
`;
@@ -24,8 +29,8 @@ export const Header = styled.div`
align-items: center;
white-space: nowrap;
border-bottom: none;
- background: ${colors.white};
- color: ${colors.gray66};
+ background: ${themeGet('colors.datePicker.background')};
+ color: ${themeGet('colors.datePicker.header')};
line-height: 32px;
font-weight: bold;
`;
@@ -36,12 +41,12 @@ export const MonthLabel = styled.div`
export const Week = styled.ul`
display: flex;
- border-bottom: 1px solid ${colors.gray14};
+ border-bottom: 1px solid ${themeGet('colors.datePicker.weekBorder')};
padding: 8px 0;
- background: ${colors.white};
+ background: ${themeGet('colors.datePicker.background')};
list-style: none;
margin: 0;
- color: ${colors.gray22};
+ color: ${themeGet('colors.datePicker.week')};
font-size: 12px;
`;
@@ -52,7 +57,7 @@ export const WeekDay = styled.li`
`;
export const Month = styled.div`
- background: ${colors.white};
+ background: ${themeGet('colors.datePicker.background')};
font-size: 14px;
@media (hover: none) {
diff --git a/components/drop-zone/component.jsx b/components/drop-zone/component.jsx
index e57b5de65..bf346d33f 100644
--- a/components/drop-zone/component.jsx
+++ b/components/drop-zone/component.jsx
@@ -13,6 +13,7 @@ export class DropZone extends PureComponent {
onDrop: PropTypes.func.isRequired,
/** The contents of the drop zone. Children will be rendered into a flex container with align-items set to center. */
children: PropTypes.node,
+ ...Paragraph.propTypes,
};
state = {
@@ -48,7 +49,7 @@ export class DropZone extends PureComponent {
};
render() {
- const { children } = this.props;
+ const { children, onDrop, ...otherProps } = this.props;
const { showHighlight } = this.state;
return (
@@ -71,6 +72,7 @@ export class DropZone extends PureComponent {
alignItems="center"
backgroundColor={showHighlight ? 'blue2' : null}
color={showHighlight ? 'blue4' : null}
+ {...otherProps}
>
{children}
diff --git a/components/dropdown/dropdown-children.jsx b/components/dropdown/dropdown-children.jsx
index b48ff1e47..08614d82d 100644
--- a/components/dropdown/dropdown-children.jsx
+++ b/components/dropdown/dropdown-children.jsx
@@ -1,8 +1,10 @@
import React, { useCallback } from 'react';
+import PropTypes from 'prop-types';
import { getConfigChild } from '../utils';
import { Box } from '../Box';
import { Text } from '../Text';
import { Checkbox } from '../check-box';
+import { UtilityButton } from '../button';
import { handledKeys, useDropdownContext } from './utils';
import * as Styled from './styled';
@@ -25,6 +27,12 @@ export function MenuItemIcon({ src, variant, children, ...iconProps }) {
MenuItemIcon.defaultProps = {
variant: 'icon',
};
+MenuItemIcon.propTypes = {
+ src: PropTypes.string,
+ variant: PropTypes.oneOf(['thumbnail', 'icon', 'avatar']),
+ children: PropTypes.node,
+ ...Box.propTypes,
+};
MenuItemIcon.childConfigComponent = 'MenuItemIcon';
export function MenuItemPrimaryText({ hasSecondaryText, children, ...textProps }) {
@@ -40,6 +48,11 @@ export function MenuItemPrimaryText({ hasSecondaryText, children, ...textProps }
);
}
+MenuItemPrimaryText.propTypes = {
+ hasSecondaryText: PropTypes.bool,
+ children: PropTypes.node.isRequired,
+ ...Text.propTypes,
+};
MenuItemPrimaryText.childConfigComponent = 'MenuItemPrimaryText';
export function MenuItemSecondaryText({ children, ...textProps }) {
@@ -49,6 +62,10 @@ export function MenuItemSecondaryText({ children, ...textProps }) {
);
}
+MenuItemSecondaryText.propTypes = {
+ children: PropTypes.node.isRequired,
+ ...Text.propTypes,
+};
MenuItemSecondaryText.childConfigComponent = 'MenuItemSecondaryText';
export const MenuItem = React.forwardRef(function MenuItem(
@@ -123,6 +140,17 @@ export const MenuItem = React.forwardRef(function MenuItem(
);
});
+MenuItem.propTypes = {
+ keyboardFocused: PropTypes.bool,
+ /** @type {(event: React.MouseEvent) => void} */
+ onClick: PropTypes.func,
+ preventDefaultOnClick: PropTypes.bool,
+ /** @type {(event: React.KeyboardEvent) => void} */
+ onKeyDown: PropTypes.func,
+ disabled: PropTypes.bool,
+ children: PropTypes.node.isRequired,
+ ...UtilityButton.propTypes,
+};
MenuItem.isFocusableChild = true;
export const MenuItemCheckbox = React.forwardRef(function MenuItemCheckbox(
@@ -155,6 +183,14 @@ export const MenuItemCheckbox = React.forwardRef(function MenuItemCheckbox(
);
});
+MenuItemCheckbox.propTypes = {
+ isChecked: PropTypes.bool,
+ /** @type {() => void} */
+ onToggle: PropTypes.func,
+ disabled: PropTypes.bool,
+ children: PropTypes.node.isRequired,
+ ...MenuItem.propTypes,
+};
MenuItemCheckbox.isFocusableChild = true;
export const MenuItemLink = React.forwardRef(function MenuItemLink(
@@ -189,6 +225,12 @@ export const MenuItemLink = React.forwardRef(function MenuItemLink(
);
});
+MenuItemLink.propTypes = {
+ disabled: PropTypes.bool,
+ href: PropTypes.string,
+ children: PropTypes.node.isRequired,
+ ...MenuItem.propTypes,
+};
MenuItemLink.isFocusableChild = true;
export function MenuItemSeparator(hrProps) {
@@ -206,6 +248,7 @@ export function MenuItemSeparator(hrProps) {
/>
);
}
+MenuItemSeparator.propTypes = Box.propTypes;
export function MenuItemTitle({ children, ...textProps }) {
return (
@@ -214,3 +257,7 @@ export function MenuItemTitle({ children, ...textProps }) {
);
}
+MenuItemTitle.propTypes = {
+ children: PropTypes.node.isRequired,
+ ...Text.propTypes,
+};
diff --git a/components/dropdown/dropdown-menu.jsx b/components/dropdown/dropdown-menu.jsx
index 99861b825..10fddbe5c 100644
--- a/components/dropdown/dropdown-menu.jsx
+++ b/components/dropdown/dropdown-menu.jsx
@@ -1,6 +1,18 @@
import React, { useCallback } from 'react';
+import PropTypes from 'prop-types';
import { Popover } from '../popover-v6';
import { useDropdownContext, useKeyboardNavigate } from './utils';
+import { elementOfType } from '../utils';
+import {
+ MenuItem,
+ MenuItemCheckbox,
+ MenuItemLink,
+ MenuItemSeparator,
+ MenuItemIcon,
+ MenuItemPrimaryText,
+ MenuItemSecondaryText,
+ MenuItemTitle,
+} from './dropdown-children';
import * as Styled from './styled';
export function DropdownMenu({ children, ...popoverProps }) {
@@ -53,3 +65,19 @@ export function DropdownMenu({ children, ...popoverProps }) {
)
);
}
+
+DropdownMenu.propTypes = {
+ children: PropTypes.arrayOf(
+ PropTypes.oneOfType([
+ elementOfType(MenuItem),
+ elementOfType(MenuItemCheckbox),
+ elementOfType(MenuItemLink),
+ elementOfType(MenuItemSeparator),
+ elementOfType(MenuItemIcon),
+ elementOfType(MenuItemPrimaryText),
+ elementOfType(MenuItemSecondaryText),
+ elementOfType(MenuItemTitle),
+ ]),
+ ).isRequired,
+ ...Popover.propTypes,
+};
diff --git a/components/dropdown/dropdown-toggle.jsx b/components/dropdown/dropdown-toggle.jsx
index 2d49a29f3..aca38d4ae 100644
--- a/components/dropdown/dropdown-toggle.jsx
+++ b/components/dropdown/dropdown-toggle.jsx
@@ -1,4 +1,5 @@
import React, { useMemo } from 'react';
+import PropTypes from 'prop-types';
import { getConfigChild } from '../utils';
import { Button, SegmentedButtonGroup } from '../button';
import { useDropdownContext, useKeyboardActivate } from './utils';
@@ -69,6 +70,23 @@ DropdownToggle.defaultProps = {
size: 'small',
variant: 'primary',
};
+DropdownToggle.propTypes = {
+ hideCarrot: PropTypes.bool,
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
+ variant: PropTypes.oneOf([
+ 'primary',
+ 'secondary',
+ 'minor',
+ 'transparent',
+ 'minorTransparent',
+ 'link',
+ 'danger',
+ 'dangerSpecial',
+ ]),
+ disabled: PropTypes.bool,
+ children: PropTypes.node.isRequired,
+ ...Button.propTypes,
+};
export function DropdownActionButton({
defaultSize,
@@ -84,3 +102,19 @@ export function DropdownActionButton({
);
}
DropdownActionButton.childConfigComponent = 'DropdownActionButton';
+DropdownActionButton.propTypes = {
+ defaultSize: PropTypes.oneOf(['small', 'medium', 'large']),
+ defaultVariant: PropTypes.oneOf([
+ 'primary',
+ 'secondary',
+ 'minor',
+ 'transparent',
+ 'minorTransparent',
+ 'link',
+ 'danger',
+ 'dangerSpecial',
+ ]),
+ defaultDisabled: PropTypes.bool,
+ children: PropTypes.element.isRequired,
+ ...Button.propTypes,
+};
diff --git a/components/popover-v6/index.jsx b/components/popover-v6/index.jsx
index 7380e434a..8740088e9 100644
--- a/components/popover-v6/index.jsx
+++ b/components/popover-v6/index.jsx
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import { usePopper } from 'faithlife-react-popper';
import { useFocusAwayHandler } from '../shared-hooks/use-focus-away-handler';
import { mergeRefs } from '../utils/merge-refs';
+import { box } from '../../theme/system';
import { PopoverContainer, PopoverArrow } from './styled';
export function usePopover(reference, options) {
@@ -99,4 +100,6 @@ Popover.propTypes = {
hideArrow: PropTypes.bool,
/** handler useful for closing popover when clicking away */
onFocusAway: PropTypes.func,
+ children: PropTypes.node.required,
+ ...box.propTypes,
};
diff --git a/components/utils/filter-props.js b/components/utils/filter-props.js
index 845a5df25..83668ef48 100644
--- a/components/utils/filter-props.js
+++ b/components/utils/filter-props.js
@@ -1,15 +1,28 @@
-export function filterChildProps(props, childPropTypes) {
- const parentProps = {};
- const childProps = {};
- const childPropNames = Object.keys(childPropTypes);
+/**
+ * Filters a collection of props by a list of names (or a prop types object).
+ *
+ * @param {object} props - A props object.
+ * @param {(string[] | object)} filterPropNames - An array of prop names to filter by (or a prop
+ * types object with keys to filter by).
+ * @returns {{ matchingProps: object, remainingProps: object }} An object containing two props
+ * objects: one containing the props that match the filter and the other containing the props that
+ * don't match.
+ */
+export function filterProps(props, filterPropNames) {
+ const filterList = Array.isArray(filterPropNames)
+ ? filterPropNames
+ : Object.keys(filterPropNames);
- for (const [propName, prop] of Object.entries(props)) {
- if (childPropNames.includes(propName)) {
- childProps[propName] = prop;
+ const matchingProps = {};
+ const remainingProps = {};
+
+ for (const [propName, propValue] of Object.entries(props)) {
+ if (filterList.includes(propName)) {
+ matchingProps[propName] = propValue;
} else {
- parentProps[propName] = prop;
+ remainingProps[propName] = propValue;
}
}
- return [parentProps, childProps];
+ return { matchingProps, remainingProps };
}
diff --git a/components/utils/index.js b/components/utils/index.js
index b3887bfc5..5232d95c3 100644
--- a/components/utils/index.js
+++ b/components/utils/index.js
@@ -1,9 +1,10 @@
export { BootstrapContainer, wrapBootstrap } from './bootstrap-container';
export { forwardClassRef } from './forwardref-wrapper';
export { TransitionStatuses, TransitionTimeouts } from './transition-group-utils';
-export { filterChildProps } from './filter-props';
+export { filterProps } from './filter-props';
export { deprecate, deprecateComponent, deprecateProp } from './deprecate';
export { getConfigProps, getConfigChild } from './get-config-props';
+export { elementOfType } from './prop-types';
/**
* Chooses the correct variant from a main variant prop and a set of boolean shortcut props.
diff --git a/components/utils/prop-types.js b/components/utils/prop-types.js
new file mode 100644
index 000000000..bc165b690
--- /dev/null
+++ b/components/utils/prop-types.js
@@ -0,0 +1,15 @@
+import PropTypes from 'prop-types';
+
+/**
+ * Creates a prop-type-checking function that validates whether the prop value is an element of a
+ * given component type.
+ *
+ * @param {React.ElementType} Component - The component type to check for.
+ * @returns A PropTypes validation function to use in a component's `propTypes` object.
+ */
+export function elementOfType(Component) {
+ // Not very intuitive, but this is the best way to check with PropTypes (see https://github.com/facebook/react/issues/2979)
+ return PropTypes.shape({
+ type: PropTypes.oneOf([Component]).isRequired,
+ });
+}
diff --git a/index-v6.js b/index-v6.js
index 6397d67b7..f3e974eda 100644
--- a/index-v6.js
+++ b/index-v6.js
@@ -1,6 +1,7 @@
export { Accordion } from './components/accordion';
export { Button, SegmentedButtonGroup } from './components/button';
export { Checkbox } from './components/check-box';
+export { DatePicker } from './components/date-picker';
export { DatePickerInput } from './components/date-picker-input';
export { Dropdown } from './components/dropdown';
export { HelpBox } from './components/help-box';
diff --git a/index.js b/index.js
index a26cf8ffa..f1a6fba3d 100644
--- a/index.js
+++ b/index.js
@@ -9,7 +9,7 @@ export { Bootstrap } from './components/bootstrap';
export { LegacyButton as Button, UtilityButton } from './components/button';
export { LegacyCheckbox as Checkbox } from './components/check-box';
export { Collapse } from './components/collapse';
-export { DatePicker } from './components/date-picker';
+export { LegacyDatePicker as DatePicker } from './components/date-picker';
export { LegacyDatePickerInput as DatePickerInput } from './components/date-picker-input';
export { DatePeriodPicker } from './components/date-period-picker';
export { DropZone } from './components/drop-zone';
diff --git a/theme/core.js b/theme/core.js
index e2a9428cb..ce3ac5753 100644
--- a/theme/core.js
+++ b/theme/core.js
@@ -201,6 +201,21 @@ colors.checkbox = {
shadowFocused: colors.input.shadowFocused,
};
+colors.datePicker = {
+ disabledOutline: colors.gray22,
+ background: colors.white,
+ header: colors.gray66,
+ week: colors.gray22,
+ weekBorder: colors.gray14,
+};
+
+colors.datePeriodPicker = {
+ background: colors.datePicker.background,
+ hoverBackground: colors.blue4,
+ hoverText: colors.white,
+ inputBorder: colors.datePicker.weekBorder,
+};
+
colors.datePickerInput = {
iconColor: colors.gray52,
};
diff --git a/theme/textStyles.js b/theme/textStyles.js
index 023105694..e0859dba5 100644
--- a/theme/textStyles.js
+++ b/theme/textStyles.js
@@ -52,59 +52,59 @@ export const textStyles = {
'12': {
fontSize: '12px',
lineHeight: '16px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
'13': {
fontSize: '13px',
lineHeight: '18px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
'14': {
fontSize: '14px',
lineHeight: '20px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
'16': {
fontSize: '16px',
lineHeight: '22px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
'18': {
fontSize: '18px',
lineHeight: '24px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
},
ui: {
'12': {
fontSize: '12px',
lineHeight: '12px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
'13': {
fontSize: '13px',
lineHeight: '13px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
'14': {
fontSize: '14px',
lineHeight: '14px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
'16': {
fontSize: '16px',
lineHeight: '16px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
'18': {
fontSize: '18px',
lineHeight: '18px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
'24': {
fontSize: '24px',
lineHeight: '24px',
- fontWeight: fontWeights.normal,
+ fontWeight: fontWeights.regular,
},
},
};