diff --git a/packages/ffe-lists-react/src/DetailListCard.js b/packages/ffe-lists-react/src/DetailListCard.js new file mode 100644 index 0000000000..ee606e18cc --- /dev/null +++ b/packages/ffe-lists-react/src/DetailListCard.js @@ -0,0 +1,23 @@ +import React from 'react'; +import classNames from 'classnames'; +import { node, string } from 'prop-types'; + +const DetailListCard = props => { + const { className, children, ...rest } = props; + + return ( +
+ {children} +
+ ); +}; + +DetailListCard.defaultProps = {}; + +DetailListCard.propTypes = { + /** Additional classnames */ + className: string, + children: node, +}; + +export default DetailListCard; diff --git a/packages/ffe-lists-react/src/DetailListCard.spec.js b/packages/ffe-lists-react/src/DetailListCard.spec.js new file mode 100644 index 0000000000..e5e871ae07 --- /dev/null +++ b/packages/ffe-lists-react/src/DetailListCard.spec.js @@ -0,0 +1,83 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import DetailListCard from './DetailListCard'; +import DetailListCardItem from './DetailListCardItem'; + +const getWrapper = props => + shallow( + + + , + ); + +const getDetailListCardItemWrapper = props => + shallow( + , + ); + +describe('', () => { + it('renders without exploding', () => { + const wrapper = getWrapper(); + expect(wrapper.exists()).toBe(true); + expect(wrapper.is('dl')).toBe(true); + }); + it('add classNames correctly', () => { + const wrapper = getWrapper({ className: 'test-class' }); + expect(wrapper.hasClass('ffe-detail-list-card')).toBe(true); + expect(wrapper.hasClass('test-class')).toBe(true); + }); +}); + +describe('', () => { + it('renders without exploding', () => { + const wrapper = getDetailListCardItemWrapper(); + expect(wrapper.is('div')).toBe(true); + expect(wrapper.hasClass('ffe-detail-list-card__item')).toBe(true); + }); + it('adds additional classNames correctly', () => { + const wrapper = getDetailListCardItemWrapper({ + className: 'test-class', + }); + expect(wrapper.hasClass('test-class')).toBe(true); + }); + + it('render correct element based on element prop', () => { + const wrapper = getDetailListCardItemWrapper({ element: 'a' }); + expect(wrapper.is('a')).toBe(true); + }); + it('adds additional props correctly', () => { + const wrapper = getDetailListCardItemWrapper({ href: '#' }); + expect(wrapper.prop('href')).toBe('#'); + }); + it('renders correct label content', () => { + const wrapper = getDetailListCardItemWrapper(); + expect(wrapper.find('dt').exists()).toBe(true); + expect( + wrapper.find('dt').hasClass('ffe-detail-list-card__item-label'), + ).toBe(true); + const wrapper2 = getDetailListCardItemWrapper({ + label: Test Label, + }); + expect(wrapper2.find('dt').contains(Test Label)).toBe( + true, + ); + }); + + it('renders correct value content', () => { + const wrapper = getDetailListCardItemWrapper(); + expect(wrapper.find('dd').exists()).toBe(true); + expect( + wrapper.find('dd').hasClass('ffe-detail-list-card__item-value'), + ).toBe(true); + const wrapper2 = getDetailListCardItemWrapper({ + value: Test Value, + }); + expect(wrapper2.find('dd').contains(Test Value)).toBe( + true, + ); + }); +}); diff --git a/packages/ffe-lists-react/src/DetailListCardItem.js b/packages/ffe-lists-react/src/DetailListCardItem.js new file mode 100644 index 0000000000..a995facea9 --- /dev/null +++ b/packages/ffe-lists-react/src/DetailListCardItem.js @@ -0,0 +1,50 @@ +import React from 'react'; +import classNames from 'classnames'; +import { string, oneOfType, node, func, elementType } from 'prop-types'; + +const DetailListCardItem = props => { + const { className, label, value, element: Element, ...rest } = props; + + const isClickable = rest.onClick || rest.href; + + return ( + +
+ {typeof label === 'string' + ? label + : React.cloneElement(label, { ...label.props })} +
+
+ {typeof value === 'string' + ? value + : React.cloneElement(value, { ...value.props })} +
+
+ ); +}; + +DetailListCardItem.defaultProps = { + element: 'div', +}; + +DetailListCardItem.propTypes = { + /** Additional classnames */ + className: string, + /** The element to render the item as */ + element: oneOfType([func, string, elementType]), + /** Content of the label / left column */ + label: oneOfType([node, string]).isRequired, + /** Content of the value / right column */ + value: oneOfType([node, string]).isRequired, +}; + +export default DetailListCardItem; diff --git a/packages/ffe-lists-react/src/index.d.ts b/packages/ffe-lists-react/src/index.d.ts index 1ee593bddb..775479bc09 100644 --- a/packages/ffe-lists-react/src/index.d.ts +++ b/packages/ffe-lists-react/src/index.d.ts @@ -32,6 +32,19 @@ export interface DescriptionListProps extends BaseListProps { horizontal?: boolean; } +export interface DetailListCardItemProps { + className?: string; + element?: HTMLElement | string | React.ElementType; + label: React.ReactNode; + value: React.ReactNode; +} +export interface DetailListCard { + className?: string; + children: + | React.ReactNode + | ((props: DetailListCardItemProps) => React.ReactNode); +} + declare class BulletList extends React.Component< BulletListProps & React.ComponentProps<'ul'>, any diff --git a/packages/ffe-lists-react/src/index.js b/packages/ffe-lists-react/src/index.js index 9b8a2f1241..d9675c4875 100644 --- a/packages/ffe-lists-react/src/index.js +++ b/packages/ffe-lists-react/src/index.js @@ -10,6 +10,8 @@ import DescriptionList from './DescriptionList'; import DescriptionListMultiCol from './DescriptionListMultiCol'; import DescriptionListTerm from './DescriptionListTerm'; import DescriptionListDescription from './DescriptionListDescription'; +import DetailListCard from './DetailListCard'; +import DetailListCardItem from './DetailListCardItem'; export { BulletList, @@ -24,6 +26,8 @@ export { DescriptionListMultiCol, DescriptionListTerm, DescriptionListDescription, + DetailListCard, + DetailListCardItem, }; export default BulletList;