diff --git a/CHANGELOG.md b/CHANGELOG.md index 16de1e2a913..1735d9cb690 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Added `textStyle="reverse"` prop to `EuiDescriptionList` as well as a class (`.eui-definitionListReverse`) for `dl`'s within `EuiText` ([#882](https://github.com/elastic/eui/pull/882)) - Added `inspect` icon ([#886](https://github.com/elastic/eui/pull/886)) +- Added `layout` prop to `EuiCard` ([#885](https://github.com/elastic/eui/pull/885)) ## [`0.0.50`](https://github.com/elastic/eui/tree/v0.0.50) diff --git a/src-docs/src/views/card/card_example.js b/src-docs/src/views/card/card_example.js index d8baa7dee52..c0492290371 100644 --- a/src-docs/src/views/card/card_example.js +++ b/src-docs/src/views/card/card_example.js @@ -9,6 +9,7 @@ import { import { EuiCode, EuiCard, + EuiCallOut, } from '../../../../src/components'; import Card from './card'; @@ -27,6 +28,10 @@ import CardBeta from './card_beta'; const cardBetaSource = require('!!raw-loader!./card_beta'); const cardBetaHtml = renderToHtml(CardBeta); +import CardLayout from './card_layout'; +const cardLayoutSource = require('!!raw-loader!./card_layout'); +const cardLayoutHtml = renderToHtml(CardLayout); + export const CardExample = { title: 'Card', sections: [{ @@ -54,6 +59,30 @@ export const CardExample = { props: { EuiCard }, demo: , }, + { + title: 'Layout', + source: [{ + type: GuideSectionTypes.JS, + code: cardLayoutSource, + }, { + type: GuideSectionTypes.HTML, + code: cardLayoutHtml, + }], + text: ( +
+

+ Most of the time, cards should read from top to bottom (vertical). However, in some cases, you may + want the icon to be to the left of the content. In this case, add the prop layout="horizontal". +

+ Horizontal layouts do not work with images, footers or textAlign. Therefore, these properties will be ignored.} + /> +
+ ), + components: { EuiCard }, + demo: , + }, { title: 'Images', source: [{ @@ -69,9 +98,9 @@ export const CardExample = { Images can be added in place of, or in conjuction with, icons. Just pass a url into the image prop and it will expand to to edges of the card.

-

- Make sure that all images are the same proportions when used in a singular row. -

+ Make sure that all images are the same proportions when used in a singular row.} + /> ), components: { EuiCard }, diff --git a/src-docs/src/views/card/card_layout.js b/src-docs/src/views/card/card_layout.js new file mode 100644 index 00000000000..d4b6854dad2 --- /dev/null +++ b/src-docs/src/views/card/card_layout.js @@ -0,0 +1,39 @@ +import React from 'react'; + +import { + EuiCard, + EuiIcon, + EuiFlexGroup, + EuiFlexItem, +} from '../../../../src/components'; + +export default () => ( + + + } + title={`Elastic Beats`} + description="Example of a card's description. Stick to one or two sentences." + onClick={() => window.alert('Card clicked')} + /> + + + } + title={`Elastic Cloud`} + description="Example of a card's description. Stick to one or two sentences." + onClick={() => window.alert('Card clicked')} + /> + + + window.alert('Card clicked')} + /> + + +); diff --git a/src/components/card/__snapshots__/card.test.js.snap b/src/components/card/__snapshots__/card.test.js.snap index 1746900d97e..dcb7ed2c7de 100644 --- a/src/components/card/__snapshots__/card.test.js.snap +++ b/src/components/card/__snapshots__/card.test.js.snap @@ -1,8 +1,31 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`EuiCard horizontal 1`] = ` +
+ + + Card title + +
+

+ Card description +

+
+
+
+`; + exports[`EuiCard icon 1`] = `
tag * 4. Fix for IE where the image correctly resizes in width but doesn't collapse it's height (https://github.com/philipwalton/flexbugs/issues/75#issuecomment-134702421) + * 5. Horizontal layouts should always top left align no matter the textAlign prop */ // EuiCard specific @@ -138,3 +139,28 @@ $euiCardTitleSize: 18px; // Hardcoded pixel value for theme parity. flex-grow: 0; /* 1 */ margin-top: $euiCardSpacing; } + +.euiCard.euiCard--horizontal { + .euiCard__content { + padding-top: $euiSizeS; // Aligns title and text a bit better and adds spacing in case of beta badge + text-align: left; /* 5 */ + } +} + +// Only change the flex direction if the card has an icon +// otherwise the button tag won't properly align contents to top +.euiCard.euiCard--horizontal.euiCard--hasIcon { + flex-direction: row; + align-items: flex-start !important; /* 5 */ + + .euiCard__top, + .euiCard__content { + width: auto; // Makes sure the top shrinks and the content grows + margin-top: 0; + } + + .euiCard__top .euiCard__icon { + margin-top: 0; + margin-right: $euiSize; + } +} diff --git a/src/components/card/card.js b/src/components/card/card.js index 80687543b50..789c09257cd 100644 --- a/src/components/card/card.js +++ b/src/components/card/card.js @@ -14,6 +14,27 @@ const textAlignToClassNameMap = { export const ALIGNMENTS = Object.keys(textAlignToClassNameMap); +const layoutToClassNameMap = { + vertical: '', + horizontal: 'euiCard--horizontal', +}; + +export const LAYOUT_ALIGNMENTS = Object.keys(layoutToClassNameMap); +const oneOfLayouts = PropTypes.oneOf(LAYOUT_ALIGNMENTS); + +const cardLayout = (props, propName, componentName, ...rest) => { + const oneOfResult = oneOfLayouts(props, propName, componentName, ...rest); + if (oneOfResult) return oneOfResult; + + if (props[propName] === 'horizontal' ) { + if (props.image || props.footer) { + return new Error( + `${componentName}: '${propName} = horizontal' cannot be used in conjunction with 'image', 'footer', or 'textAlign'.` + ); + } + } +}; + export const EuiCard = ({ className, description, @@ -28,20 +49,23 @@ export const EuiCard = ({ betaBadgeLabel, betaBadgeTooltipContent, betaBadgeTitle, + layout, ...rest, }) => { const classes = classNames( 'euiCard', textAlignToClassNameMap[textAlign], + layoutToClassNameMap[layout], { 'euiCard--isClickable': onClick || href || isClickable, 'euiCard--hasBetaBadge': betaBadgeLabel, + 'euiCard--hasIcon': icon, }, className, ); let imageNode; - if (image) { + if (image && layout === 'vertical') { imageNode = ( ); @@ -63,7 +87,7 @@ export const EuiCard = ({ } let optionalCardTop; - if (image || icon) { + if (imageNode || iconNode) { optionalCardTop = ( {imageNode} @@ -102,9 +126,11 @@ export const EuiCard = ({ - - {footer} - + {layout === 'vertical' && + + {footer} + + } ); }; @@ -136,6 +162,11 @@ EuiCard.propTypes = { href: PropTypes.string, textAlign: PropTypes.oneOf(ALIGNMENTS), + /** + * Change to "horizontal" if you need the icon to be left of the content + */ + layout: cardLayout, + /** * Add a badge to the card to label it as "Beta" or other non-GA state */ @@ -154,4 +185,5 @@ EuiCard.propTypes = { EuiCard.defaultProps = { textAlign: 'center', + layout: 'vertical', }; diff --git a/src/components/card/card.test.js b/src/components/card/card.test.js index 649127076cf..b31fb6ee137 100644 --- a/src/components/card/card.test.js +++ b/src/components/card/card.test.js @@ -33,6 +33,19 @@ describe('EuiCard', () => { .toMatchSnapshot(); }); + test('horizontal', () => { + const component = render( + + ); + + expect(component) + .toMatchSnapshot(); + }); + describe('onClick', () => { it('supports onClick as a link', () => { const handler = jest.fn(); diff --git a/src/components/text/_text.scss b/src/components/text/_text.scss index 74b3cf24208..9802edf818c 100644 --- a/src/components/text/_text.scss +++ b/src/components/text/_text.scss @@ -141,6 +141,7 @@ content: ""; height: 2px; width: 50%; + right: 0; transform: translateX(-50%); background: $euiColorDarkShade; }