Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
feat: add tag component
Browse files Browse the repository at this point in the history
  • Loading branch information
cooper-joe authored and ismay committed Mar 2, 2020
1 parent 2931fc5 commit 4e068dc
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 2 deletions.
9 changes: 9 additions & 0 deletions cypress/integration/Tag/Accepts_icon.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Feature: The Tag accepts an icon prop

Scenario: Tag has no icon by default
Given a default Tag is rendered
Then the icon will not be visible

Scenario: Tag with icon
Given a Tag with an icon is rendered
Then the icon will be visible
21 changes: 21 additions & 0 deletions cypress/integration/Tag/Accepts_icon/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Given, Then } from 'cypress-cucumber-preprocessor/steps'

Given('a default Tag is rendered', () => {
cy.visitStory('Tag', 'Without icon')
cy.get('[data-test="dhis2-uicore-tag"]').should('be.visible')
})

Given('a Tag with an icon is rendered', () => {
cy.visitStory('Tag', 'With icon')
cy.get('[data-test="dhis2-uicore-tag"]').should('be.visible')
})

Then('the icon will not be visible', () => {
cy.get('[data-test="dhis2-uicore-tag-icon"]').should('not.be.visible')
})

Then('the icon will be visible', () => {
cy.get('[data-test="dhis2-uicore-tag-icon"]')
.contains('Icon')
.should('be.visible')
})
5 changes: 5 additions & 0 deletions cypress/integration/Tag/Accepts_text.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Feature: The Tag displays text

Scenario: Standard Tag with text
Given a Tag with text is rendered
Then the text will be visible
12 changes: 12 additions & 0 deletions cypress/integration/Tag/Accepts_text/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Given, Then } from 'cypress-cucumber-preprocessor/steps'

Given('a Tag with text is rendered', () => {
cy.visitStory('Tag', 'With text')
cy.get('[data-test="dhis2-uicore-tag"]').should('be.visible')
})

Then('the text will be visible', () => {
cy.get('[data-test="dhis2-uicore-tag-text"]')
.contains('Text content')
.should('be.visible')
})
134 changes: 134 additions & 0 deletions src/Tag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React from 'react'
import propTypes from '@dhis2/prop-types'
import cx from 'classnames'

import { TagIcon } from './Tag/TagIcon.js'
import { TagText } from './Tag/TagText.js'
import { colors } from './theme.js'
;('') // TODO: https://github.com/jsdoc/jsdoc/issues/1718

/**
* @module
* @param {Tag.PropTypes} props
* @returns {React.Component}
* @example import { Tag } from @dhis2/ui-core
* @see Specification: {@link https://github.com/dhis2/design-system/blob/master/atoms/tag.md|Design system}
* @see Live demo: {@link /demo/?path=/story/tag--default|Storybook}
*/

export const Tag = ({
neutral,
negative,
positive,
icon,
bold,
className,
dataTest,
children,
}) => (
<div
data-test={dataTest}
className={cx(className, {
neutral,
positive,
negative,
bold,
})}
>
{icon && <TagIcon dataTest={`${dataTest}-icon`}>{icon}</TagIcon>}
<TagText dataTest={`${dataTest}-text`}>{children}</TagText>
<style jsx>
{`
div {
padding: 5px 6px 3px;
border-radius: 3px;
background-color: ${colors.grey300};
fill: ${colors.grey700};
color: ${colors.grey900};
max-width: 240px;
display: inline-flex;
font-size: 13px;
line-height: 12px;
height: 23px;
}
.negative {
background-color: ${colors.red100};
fill: ${colors.red800};
color: ${colors.red900};
}
.neutral {
background-color: ${colors.blue100};
fill: ${colors.blue800};
color: ${colors.blue900};
}
.positive {
background-color: ${colors.green100};
fill: ${colors.green800};
color: ${colors.green900};
}
.bold {
font-weight: 700;
background-color: ${colors.grey700};
color: ${colors.white};
fill: ${colors.white};
}
.bold.neutral {
background-color: ${colors.blue800};
color: ${colors.blue050};
fill: ${colors.white};
}
.bold.positive {
background-color: ${colors.green700};
color: ${colors.green050};
fill: ${colors.white};
}
.bold.negative {
background-color: ${colors.red700};
color: ${colors.red050};
fill: ${colors.white};
}
`}
</style>
</div>
)

const tagVariantPropType = propTypes.mutuallyExclusive(
['neutral', 'positive', 'negative'],
propTypes.bool
)

Tag.defaultProps = {
dataTest: 'dhis2-uicore-tag',
}

/**
* @typedef {Object} PropTypes
* @static
*
* @prop {boolean} [bold]
* @prop {Node} [children]
* @prop {string} [className]
* @prop {string} [dataTest]
* @prop {Node} [icon]
* @prop {boolean} [neutral] - `neutral`, `positive`, and
* `negative` are mutually exclusive boolean props
* @prop {boolean} [positive]
* @prop {boolean} [negative]
*/

Tag.propTypes = {
bold: propTypes.bool,
children: propTypes.string,
className: propTypes.string,
dataTest: propTypes.string,
icon: propTypes.node,
negative: tagVariantPropType,
neutral: tagVariantPropType,
positive: tagVariantPropType,
}
20 changes: 20 additions & 0 deletions src/Tag/TagIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import propTypes from '@dhis2/prop-types'
import React from 'react'

export const TagIcon = ({ children, dataTest }) => (
<div data-test={dataTest}>
{children}
<style jsx>{`
margin-right: 4px;
width: 12px;
height: 12px;
overflow: hidden;
flex-shrink: 0;
`}</style>
</div>
)

TagIcon.propTypes = {
dataTest: propTypes.string.isRequired,
children: propTypes.node,
}
18 changes: 18 additions & 0 deletions src/Tag/TagText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import propTypes from '@dhis2/prop-types'
import React from 'react'

export const TagText = ({ children, dataTest }) => (
<span data-test={dataTest}>
{children}
<style jsx>{`
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`}</style>
</span>
)

TagText.propTypes = {
dataTest: propTypes.string.isRequired,
children: propTypes.node,
}
58 changes: 58 additions & 0 deletions src/__demo__/Tag.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react'

import { Tag } from '../Tag.js'

export default { title: 'Tag', component: Tag }

export const Default = () => <Tag>Dog</Tag>

export const WithIcon = () => <Tag icon={<ExampleIcon />}>Dog</Tag>

export const Neutral = () => <Tag neutral>Dog</Tag>

export const Positive = () => <Tag positive>Dog</Tag>

export const Negative = () => <Tag negative>Dog</Tag>

export const Bold = () => <Tag bold>Dog</Tag>

export const WithClippedOversizedIcon = () => (
<Tag icon={<ExampleLargeIcon />}>Dog</Tag>
)

export const WithClippedLongText = () => (
<Tag icon={<ExampleIcon />}>
I am long text, therefore I get clipped before I finish
</Tag>
)

const ExampleIcon = () => (
<svg
height="12"
viewBox="0 0 12 12"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<g fill="inherit">
<path d="m6.00003329 5c-.40449432-.00016901-.76925356.24336235-.92416402.61701786-.15491047.3736555-.06945918.80383558.21650172 1.08991608s.71610521.37171165 1.08982546.21695745c.37372025-.15475421.61740424-.51941158.61740424-.92390594.00011082-.26517416-.10515147-.51952543-.29261873-.70707109-.18746725-.18754566-.44177449-.29291436-.70694867-.29291436z" />
<path d="m9.10419761 8.52484537c.76535472-.94655629 1.06135379-2.18836523.80523589-3.37821761-.05634956-.27032079-.32125073-.44379594-.59167333-.38746761-.2704226.05632834-.4439631.32112977-.38761355.59145056.19496765.89486787-.02567692 1.82986482-.60017581 2.5432874-.72092529.88419157-1.88113225 1.28314793-2.99364655 1.02941488-1.11251431-.25373305-1.98479962-1.11624341-2.25073114-2.22550911-.26593151-1.10926569.12051398-2.27331263.99713349-3.00355471.69583114-.57340823 1.61098701-.80724221 2.4967314-.63794667.17734061.03661596.36062942-.02545013.47919689-.16226782.11856746-.1368177.15389197-.32701448.09235365-.49725733s-.21032645-.29393785-.38899706-.32339268c-1.92234735-.37212623-3.83170501.70111683-4.5123533 2.53637752s.06739468 3.8933194 1.76783755 4.86377602c1.70044287.97045659 3.85367697.56818039 5.08870246-.95069268z" />
<path d="m5.99960069 0c-1.97689694.00113-3.82650358.97550158-4.94544231 2.60525583-1.11893874 1.62975425-1.3637448 3.70593215-.65455769 5.55124417.05611465.17686038.20555481.3081025.38818046.34090985.18262565.03280736.36840126-.03821571.48256414-.18448699.11416287-.14627127.13793722-.34373427.0617554-.51292286-.85968765-2.23032693-.01933119-4.75713774 2.00488351-6.02834816s4.66524393-.93071599 6.30084518.81233583c1.63560122 1.74305182 1.80759312 4.40037955.41033032 6.33972937-1.39726277 1.93934986-3.97236477 2.61745126-6.14355901 1.61778296-.16228789-.0750258-.35227387-.0577729-.49839205.0452597-.14611818.1030325-.22616973.2761916-.21000001.45425.01616973.1780583.12610416.3339645.28839206.4089903.78810364.364813 1.64655922.5525468 2.515.5500256 3.31370847 0 5.99999991-2.68631714 5.99999991-6.0000256s-2.68629144-6-5.99999991-6z" />
<path d="m8.99960069 3.5c0-.20225265-.1218571-.38459013-.30873435-.46195903-.18687725-.07736891-.40196034-.03452776-.54492981.1085414-.14296946.14306916-.18566067.35818204-.10816151.5450053.07749917.18682326.25992156.30855316.46218139.30841233.27600227-.00019246.49964428-.22399048.49964428-.5z" />
<circle cx="1.499601" cy="9.5" r="1" />
</g>
</svg>
)

const ExampleLargeIcon = () => (
<svg
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m12 2c-5.52 0-10 4.48-10 10s4.48 10 10 10 10-4.48 10-10-4.48-10-10-10zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5s2.01-4.5 4.5-4.5 4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-5.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"
fill="inherit"
/>
</svg>
)
8 changes: 8 additions & 0 deletions src/__e2e__/Tag.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { Tag } from '../index.js'

storiesOf('Tag', module)
.add('Without icon', () => <Tag>Default</Tag>)
.add('With icon', () => <Tag icon={<span>Icon</span>}>Default</Tag>)
.add('With text', () => <Tag>Text content</Tag>)
5 changes: 3 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,23 @@ export { Chip } from './Chip.js'
export { CircularLoader } from './CircularLoader.js'
export { ComponentCover } from './ComponentCover.js'
export { Divider } from './Divider.js'
export { Field } from './Field.js'
export { FieldSet } from './FieldSet.js'
export { FileInput } from './FileInput.js'
export { FileInputField } from './FileInputField.js'
export { FileInputFieldWithList } from './FileInputFieldWithList.js'
export { FileList } from './FileList.js'
export { FileListItem } from './FileListItem.js'
export { FileListPlaceholder } from './FileListPlaceholder.js'
export { Field } from './Field.js'
export { Help } from './Help.js'
export { Input } from './Input.js'
export { InputField } from './InputField.js'
export { Label } from './Label.js'
export { Legend } from './Legend.js'
export { LinearLoader } from './LinearLoader.js'
export { Logo, LogoIcon, LogoIconWhite, LogoWhite } from './Logo.js'
export { MenuList } from './MenuList.js'
export { MenuItem } from './MenuItem.js'
export { MenuList } from './MenuList.js'
export { Radio } from './Radio.js'
export { RadioGroup } from './RadioGroup.js'
export { RadioGroupField } from './RadioGroupField.js'
Expand All @@ -53,6 +53,7 @@ export { Switch } from './Switch.js'
export { SwitchField } from './SwitchField.js'
export { SwitchGroup } from './SwitchGroup.js'
export { SwitchGroupField } from './SwitchGroupField.js'
export { Tag } from './Tag.js'
export { TextArea } from './TextArea.js'
export { TextAreaField } from './TextAreaField.js'

Expand Down

0 comments on commit 4e068dc

Please sign in to comment.