diff --git a/packages/terra-clinical-item-view/src/ItemView.jsx b/packages/terra-clinical-item-view/src/ItemView.jsx index 9e16fd91c..23be5cb04 100644 --- a/packages/terra-clinical-item-view/src/ItemView.jsx +++ b/packages/terra-clinical-item-view/src/ItemView.jsx @@ -26,9 +26,13 @@ const AccessoryAlignments = { const propTypes = { /** - * The column layout in which to present the displays. One of `oneColumn`, `twoColumn`. + * The visual column layout in which to present the displays. One of `oneColumn`, `twoColumn`. */ layout: PropTypes.oneOf(['oneColumn', 'twoColumns']), + /** + * Determines whether the displays are programatically separated by row or as true columns when layout is set to `twoColumns`. + */ + trueColumn: PropTypes.bool, /** * The text color emphasis when using two columns. One of `default`, `start`. */ @@ -73,6 +77,7 @@ const propTypes = { const defaultProps = { layout: Layouts.ONE_COLUMN, + trueColumn: true, textEmphasis: TextEmphasisTypes.DEFAULT, overrideDefaultStyling: false, isTruncated: false, @@ -120,19 +125,19 @@ const defaultEmphasisContentClassesFromIndexes = (rowIndex, rowCount) => { return [contentSize, contentColor]; }; -const startEmphasisContentClassesFromIndexes = (rowIndex, rowCount, columnIndex) => { - if (columnIndex > 0 || rowIndex >= 2) { +const startEmphasisContentClassesFromIndexes = (rowIndex, rowCount, contentIndex) => { + if (contentIndex > 0 || rowIndex >= 2) { return ['content-secondary-size', 'content-secondary-color']; } return defaultEmphasisContentClassesFromIndexes(rowIndex, rowCount); }; -const classesForContent = (rowIndex, rowCount, columnIndex, emphasis) => { +const classesForContent = (rowIndex, rowCount, contentIndex, emphasis) => { let classes; if (emphasis === TextEmphasisTypes.START) { - classes = startEmphasisContentClassesFromIndexes(rowIndex, rowCount, columnIndex); + classes = startEmphasisContentClassesFromIndexes(rowIndex, rowCount, contentIndex); } else { classes = defaultEmphasisContentClassesFromIndexes(rowIndex, rowCount); } @@ -140,6 +145,45 @@ const classesForContent = (rowIndex, rowCount, columnIndex, emphasis) => { return ['content'].concat(classes); }; +const twoColumnGrouping = (displays) => { + let count = 0; + const displayGroups = []; + const primaryColumn = []; + const secondaryColumn = []; + + while (displays.length) { + count += 1; + + if (count % 2 === 0) { + secondaryColumn.push(displays.splice(0, 1)); + } else { + primaryColumn.push(displays.splice(0, 1)); + } + } + + displayGroups.push(primaryColumn); + displayGroups.push(secondaryColumn); + + return displayGroups; +}; + +const renderRow = (row, rowIndex, rowCount, emphasis) => { + const rowKey = rowIndex; + return ( +
+ {row.map((display, displayIndex) => { + const displayKey = displayIndex; + const contentClasses = classesForContent(rowIndex, rowCount, displayIndex, emphasis); + return ( +
+ {display} +
+ ); + })} +
+ ); +}; + const renderColumn = (displayGroup, displayGroupIndex, emphasis, overrideDefaultStyling) => { const columnKey = displayGroupIndex; const displayCount = displayGroup.length; @@ -173,31 +217,32 @@ const renderColumn = (displayGroup, displayGroupIndex, emphasis, overrideDefault ); }; -const renderView = (displays, layout, emphasis, overrideDefaultStyling) => { +const renderView = (displays, layout, emphasis, overrideDefaultStyling, trueColumn) => { if (displays === null || displays === undefined || !displays.length) { return undefined; } - const primaryColumn = []; - const displayGroups = []; + let displayGroups = []; const displaysSlice = displays.slice(0, 8); + const primaryColumn = []; if (layout === Layouts.TWO_COLUMNS) { - let count = 0; - const secondaryColumn = []; - - while (displaysSlice.length) { - count += 1; - - if (count % 2 === 0) { - secondaryColumn.push(displaysSlice.splice(0, 1)); - } else { - primaryColumn.push(displaysSlice.splice(0, 1)); + if (trueColumn) { + displayGroups = twoColumnGrouping(displaysSlice); + } else { + while (displaysSlice.length) { + displayGroups.push(displaysSlice.splice(0, 2)); } - } - displayGroups.push(primaryColumn); - displayGroups.push(secondaryColumn); + return ( +
+ {displayGroups.map((displayRow, rowIndex) => { + const row = renderRow(displayRow, rowIndex, displayGroups.length, emphasis); + return row; + })} +
+ ); + } } else { while (displaysSlice.length) { primaryColumn.push(displaysSlice.splice(0, 1)); @@ -216,8 +261,21 @@ const renderView = (displays, layout, emphasis, overrideDefaultStyling) => { ); }; +const isDisplaysTruncated = (displays) => { + const displaysSlice = displays.slice(0, 8); + + for (let i = 0; i < displaysSlice.length; i += 1) { + if (displaysSlice[i].props.isTruncated === true) { + return true; + } + } + + return false; +}; + const ItemView = ({ layout, + trueColumn, textEmphasis, overrideDefaultStyling, isTruncated, @@ -231,13 +289,16 @@ const ItemView = ({ ...customProps }) => { const theme = React.useContext(ThemeContext); + const isTrueTwoColumnView = layout === Layouts.TWO_COLUMNS && trueColumn; + const isTruncatedDisplay = isTruncated || isDisplaysTruncated(displays); const viewClassNames = classNames( cx( 'item-view', { 'is-truncated': isTruncated }, { 'one-column': layout === Layouts.ONE_COLUMN }, - { 'two-columns': (layout === Layouts.TWO_COLUMNS && !isTruncated) }, - { 'truncated-two-columns': (layout === Layouts.TWO_COLUMNS && isTruncated) }, + { 'two-columns': isTrueTwoColumnView && !isTruncatedDisplay }, + { 'truncated-two-columns': isTrueTwoColumnView && isTruncatedDisplay }, + { 'two-columns-by-row': layout === Layouts.TWO_COLUMNS && !trueColumn }, theme.className, ), customProps.className, @@ -247,7 +308,7 @@ const ItemView = ({
{renderAccessory(startAccessory, reserveStartAccessorySpace, accessoryAlignment, 'start')}
- {renderView(displays, layout, textEmphasis, overrideDefaultStyling)} + {renderView(displays, layout, textEmphasis, overrideDefaultStyling, trueColumn)} {comment}
{renderAccessory(endAccessory, false, accessoryAlignment, 'end')} diff --git a/packages/terra-clinical-item-view/src/ItemView.module.scss b/packages/terra-clinical-item-view/src/ItemView.module.scss index 361536565..53ce8065a 100644 --- a/packages/terra-clinical-item-view/src/ItemView.module.scss +++ b/packages/terra-clinical-item-view/src/ItemView.module.scss @@ -78,6 +78,11 @@ } } + .row { + display: flex; + width: 100%; + } + .is-truncated, .is-truncated [data-terra-clinical-item-display-text] { @include terra-clinical-text-truncate; @@ -99,6 +104,25 @@ } } + .two-columns-by-row { + .content:nth-child(odd) { + flex: 1 1 auto; + float: left; + justify-content: flex-start; + } + + .content:nth-child(even) { + @include terra-text-align-end(); + flex: 1 0 auto; + float: right; + justify-content: flex-end; + // Set a max-width and disable flexbox-squishing on the right-most displays. This will ensure that they aren't + // prematurely squished and maximize the available space for all labels. + max-width: 60%; + padding-left: 5px; + } + } + .truncated-two-columns { .primary-column { flex: 1 1 auto; diff --git a/packages/terra-clinical-item-view/src/terra-dev-site/doc/clinical-item-view/clinicalItemView.1.doc.mdx b/packages/terra-clinical-item-view/src/terra-dev-site/doc/clinical-item-view/clinicalItemView.1.doc.mdx index c28080b2a..bd4a73aac 100644 --- a/packages/terra-clinical-item-view/src/terra-dev-site/doc/clinical-item-view/clinicalItemView.1.doc.mdx +++ b/packages/terra-clinical-item-view/src/terra-dev-site/doc/clinical-item-view/clinicalItemView.1.doc.mdx @@ -3,6 +3,7 @@ import { Badge } from 'terra-clinical-item-view/package.json?dev-site-package'; import ItemViewStandard from '../example/ItemViewStandard?dev-site-example'; import ItemViewTwoColumn from '../example/ItemViewTwoColumn?dev-site-example'; import ItemViewTwoColumnStart from '../example/ItemViewTwoColumnStart?dev-site-example'; +import ItemViewTwoColumnByRow from '../example/ItemViewTwoColumnByRow?dev-site-example'; import ItemViewComment from '../example/ItemViewComment?dev-site-example'; import ItemViewAll from '../example/ItemViewAll?dev-site-example'; import ItemViewAllTopAligned from '../example/ItemViewAllTopAligned?dev-site-example'; @@ -37,6 +38,10 @@ import ItemView from 'terra-clinical-item-view'; title="ItemView - Two Column Layout" description="When including icons as part of an ''', it is recommended to use the iconAlignment='inline' prop for best alignment and wrapping with the text." /> + diff --git a/packages/terra-clinical-item-view/src/terra-dev-site/doc/example/ItemViewTwoColumnByRow.jsx b/packages/terra-clinical-item-view/src/terra-dev-site/doc/example/ItemViewTwoColumnByRow.jsx new file mode 100644 index 000000000..e3b01a274 --- /dev/null +++ b/packages/terra-clinical-item-view/src/terra-dev-site/doc/example/ItemViewTwoColumnByRow.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import IconBriefcase from 'terra-icon/lib/icon/IconBriefcase'; +import IconPerson from 'terra-icon/lib/icon/IconPerson'; +import ItemView from 'terra-clinical-item-view'; + +const display1 = } iconAlignment="inline" text="Asif Khan" />; +const display2 = } iconAlignment="inline" text="Care Position: Primary" />; +const display3 = ; +const display4 = ; +const display5 = ; +const display6 = ; +const displays = [display1, display2, display3, display4, display5, display6]; + +export default () => ; diff --git a/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/DisplaysItemViewByRow.test.jsx b/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/DisplaysItemViewByRow.test.jsx new file mode 100644 index 000000000..3757b1d32 --- /dev/null +++ b/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/DisplaysItemViewByRow.test.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import IconAlert from 'terra-icon/lib/icon/IconAlert'; +import ItemView from '../../../ItemView'; + +const display1 = } iconAlignment="inline" text="display1 Text display1 Text display1 Text display1 Text display1 Text display1 Text display1 Text display1 Text" key="123" />; +const display2 = } iconAlignment="inline" text="display2 Text display2 Text display2 Text display2 Text display2 Text display2 Text display2 Text display2 Text display2 Text" key="124" />; +const display3 = ; +const display4 = ; +const display5 = ; +const display6 = ; +const displays = [display1, display2, display3, display4, display5, display6]; + +const views = () => ( +
+

Two Column Layout by Row

+ +
+

Two Column Layout by Row with Start TextEmphasis

+ +
+); + +export default views; diff --git a/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/OverflowDisplaysItemView.test.jsx b/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/OverflowDisplaysItemView.test.jsx index 8b42dc607..a3c359002 100644 --- a/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/OverflowDisplaysItemView.test.jsx +++ b/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/OverflowDisplaysItemView.test.jsx @@ -14,6 +14,11 @@ const display4 = ; const display5 = ; const display6 = ; const displays = [display1, display2, display3, display4, display5, display6]; +const isTruncatedDisplay1 = ; +const isTruncatedDisplay2 = ; +const partialTruncatedDisplays = [isTruncatedDisplay1, isTruncatedDisplay2, display3, display4, display5, display6]; +const leftPartialTruncatedDisplays = [isTruncatedDisplay1, display2, display3, display4, display5, display6]; +const rightPartialTruncatedDisplays = [display1, isTruncatedDisplay2, display3, display4, display5, display6]; const accessoryStart = id => ; const accessoryEnd = id => ; @@ -24,14 +29,26 @@ const views = () => (

Applied width of 900px to show the default vs truncated styling.


Full Examples - Default

- +
- +

Full Examples - Truncated

- +
- + +
+

Partial Truncation Examples - Single Column

+ +
+

Partial Truncation Examples - Two Column Left and Right Truncated

+ +
+

Partial Truncation Examples - Two Column Left Truncated

+ +
+

Partial Truncation Examples - Two Column Right Truncated

+
); diff --git a/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/OverflowDisplaysItemViewByRow.test.jsx b/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/OverflowDisplaysItemViewByRow.test.jsx new file mode 100644 index 000000000..4fef5acff --- /dev/null +++ b/packages/terra-clinical-item-view/src/terra-dev-site/test/clinical-item-view/OverflowDisplaysItemViewByRow.test.jsx @@ -0,0 +1,49 @@ +import React from 'react'; +import IconAlert from 'terra-icon/lib/icon/IconAlert'; +import IconInformation from 'terra-icon/lib/icon/IconInformation'; +import classNames from 'classnames/bind'; +import ItemView from '../../../ItemView'; +import styles from './ItemViewCommon.test.module.scss'; + +const cx = classNames.bind(styles); + +const display1 = ; +const display2 = ; +const display3 = ; +const display4 = ; +const display5 = ; +const display6 = ; +const displays = [display1, display2, display3, display4, display5, display6]; +const isTruncatedDisplay1 = ; +const isTruncatedDisplay2 = ; +const partialTruncatedDisplays = [isTruncatedDisplay1, isTruncatedDisplay2, display3, display4, display5, display6]; +const leftPartialTruncatedDisplays = [isTruncatedDisplay1, display2, display3, display4, display5, display6]; +const rightPartialTruncatedDisplays = [display1, isTruncatedDisplay2, display3, display4, display5, display6]; + +const accessoryStart = id => ; +const accessoryEnd = id => ; +const comment = id => ; + +const views = () => ( +
+

Applied width of 900px to show the default vs truncated styling.

+
+

Default ItemView

+ +
+

Truncated ItemView

+ +
+

The next examples include when ItemView has `isTruncated` set to `false` while the ItemDisplays passed in to the ItemView have the `isTruncated` prop set to `true`.

+

Left and Right Displays Truncated

+ +
+

Left Display Truncated

+ +
+

Right Display Truncated

+ +
+); + +export default views; diff --git a/packages/terra-clinical-item-view/tests/jest/ItemView.test.jsx b/packages/terra-clinical-item-view/tests/jest/ItemView.test.jsx index 7d0ff01d3..fad1b7aaa 100644 --- a/packages/terra-clinical-item-view/tests/jest/ItemView.test.jsx +++ b/packages/terra-clinical-item-view/tests/jest/ItemView.test.jsx @@ -210,6 +210,24 @@ it('should render two columns with an odd number of displays', () => { expect(itemView).toMatchSnapshot(); }); +it('should render two columns with 8 displays when trueColumn is false', () => { + const display1 = shallowWithIntl(); + const display2 = shallowWithIntl(); + const display3 = shallowWithIntl(); + const display4 = shallowWithIntl(); + const display5 = shallowWithIntl(); + const display6 = shallowWithIntl(); + const display7 = shallowWithIntl(); + const display8 = shallowWithIntl(); + const display9 = shallowWithIntl(); + + const displays = [display1, display2, display3, display4, display5, display6, display7, display8, display9]; + const itemView = shallow(); + expect(itemView.find('ItemDisplay')).toHaveLength(8); + expect(itemView.find('div.two-columns-by-row')).toHaveLength(1); + expect(itemView).toMatchSnapshot(); +}); + it('correctly applies the theme context className', () => { jest.spyOn(React, 'useContext') .mockReturnValue({ diff --git a/packages/terra-clinical-item-view/tests/jest/__snapshots__/ItemView.test.jsx.snap b/packages/terra-clinical-item-view/tests/jest/__snapshots__/ItemView.test.jsx.snap index c5d3d97c5..54e0fca7c 100644 --- a/packages/terra-clinical-item-view/tests/jest/__snapshots__/ItemView.test.jsx.snap +++ b/packages/terra-clinical-item-view/tests/jest/__snapshots__/ItemView.test.jsx.snap @@ -1108,6 +1108,353 @@ exports[`should render two columns with 8 displays 1`] = ` `; +exports[`should render two columns with 8 displays when trueColumn is false 1`] = ` +
+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+`; + exports[`should render two columns with an odd number of displays 1`] = `
{ - it('with one column displays', () => { + it('with displays', () => { browser.url('/raw/tests/terra-clinical-item-view/clinical-item-view/displays-item-view'); Terra.validates.element('with one column', { selector: '#test-displays' }); @@ -7,6 +7,13 @@ Terra.describeViewports('Clinical Item View', ['tiny', 'small', 'medium', 'large Terra.validates.element('with two column and start displays', { selector: '#test-displays-two-start' }); }); + it('with displays by row', () => { + browser.url('/raw/tests/terra-clinical-item-view/clinical-item-view/displays-item-view-by-row'); + + Terra.validates.element('with two columns by row', { selector: '#test-displays-two-row' }); + Terra.validates.element('with two columns and start displays by row', { selector: '#test-displays-two-row-start' }); + }); + it('with accessories', () => { browser.url('/raw/tests/terra-clinical-item-view/clinical-item-view/accessory-item-view'); @@ -20,13 +27,19 @@ Terra.describeViewports('Clinical Item View', ['tiny', 'small', 'medium', 'large Terra.validates.element('truncated comment', { selector: '#ItemView2' }); }); - it('with the full example word wrap', () => { + it('with word wrap', () => { browser.url('/raw/tests/terra-clinical-item-view/clinical-item-view/overflow-displays-item-view'); Terra.validates.element('with word wrap in one column', { selector: '#ItemView-one-wrap' }); Terra.validates.element('with word wrap in two columns', { selector: '#ItemView-two-wrap' }); }); + it('with word wrap by row', () => { + browser.url('/raw/tests/terra-clinical-item-view/clinical-item-view/overflow-displays-item-view-by-row'); + + Terra.validates.element('with word wrap in two columns by row', { selector: '#ItemView-by-row-two-wrap' }); + }); + it('with override default styling', () => { browser.url('/raw/tests/terra-clinical-item-view/clinical-item-view/styling-override-item-view'); @@ -35,10 +48,19 @@ Terra.describeViewports('Clinical Item View', ['tiny', 'small', 'medium', 'large }); Terra.describeViewports('Clinical Item View', ['enormous'], () => { - it('with the full example truncated - one truncated', () => { + it('with truncation', () => { browser.url('/raw/tests/terra-clinical-item-view/clinical-item-view/overflow-displays-item-view'); - Terra.validates.element('with truncation in one column', { selector: '#ItemView-one-wrap' }); - Terra.validates.element('with truncation in two columns', { selector: '#ItemView-two-wrap' }); + Terra.validates.element('with truncation in one column', { selector: '#ItemView-one-truncate' }); + Terra.validates.element('with truncation in two columns', { selector: '#ItemView-two-truncate' }); }); }); + +Terra.describeViewports('Clinical Item View', ['enormous'], () => { + it('with by row truncation', () => { + browser.url('/raw/tests/terra-clinical-item-view/clinical-item-view/overflow-displays-item-view-by-row'); + + Terra.validates.element('with truncation in two columns by row', { selector: '#ItemView-by-row-two-truncate' }); + }); +}); +