From 81647e656110b1329332e59a65e3fd35ad7c343c Mon Sep 17 00:00:00 2001 From: Ben Penner <75634404+TimeBones@users.noreply.github.com> Date: Fri, 11 Feb 2022 15:55:28 -0600 Subject: [PATCH] Add pill component (#5) Adding a Pill component to use for concise inline status messages. --- src/components/pill/Pill.css | 100 +++++++++++++++++ src/components/pill/Pill.js | 75 +++++++++++++ src/components/pill/Pill.test.js | 104 ++++++++++++++++++ src/components/pill/Readme.md | 61 ++++++++++ .../pill/__snapshots__/Pill.test.js.snap | 24 ++++ src/components/pill/timesIcon.js | 2 + src/index.js | 1 + styleguide.config.js | 1 + 8 files changed, 368 insertions(+) create mode 100644 src/components/pill/Pill.css create mode 100644 src/components/pill/Pill.js create mode 100644 src/components/pill/Pill.test.js create mode 100644 src/components/pill/Readme.md create mode 100644 src/components/pill/__snapshots__/Pill.test.js.snap create mode 100644 src/components/pill/timesIcon.js diff --git a/src/components/pill/Pill.css b/src/components/pill/Pill.css new file mode 100644 index 0000000..1576a8b --- /dev/null +++ b/src/components/pill/Pill.css @@ -0,0 +1,100 @@ +.stx-pill { + display: inline-flex; + vertical-align: middle; + align-items: center; + margin: 0px 0.3em; + border: 1px solid #5C6670; + background-color: #ECEEF0; + color: #394046; + font-size: 0.8125rem; + font-weight: 500; + padding: 0.0625rem 0.375rem; + line-height: 1rem; + border-radius: 0.625rem; +} + +.stx-pill--with-info { + border-color: #3B63FF; + background-color: #E8ECFF; + color: #264BD9; +} + +.stx-pill--with-warning { + border-color: #E16D00; + background-color: #F0DAFF; + color: #B25600; +} + +.stx-pill--with-success { + border-color: #008757; + background-color: #DEF6EE; + color: #00633F; +} + +.stx-pill--with-alert { + border-color: #E41244; + background-color: #FFEBF0; + color: #B4002B; +} + +.stx-pill--is-clickable { + cursor: pointer; +} + +.stx-pill--is-minimized { + padding: 0; + width: 0.5rem; + height: 0.5rem; + border-radius: 0.5rem; +} + +.stx-pill--is-minimized > .bv-pill__icon, +.stx-pill--is-minimized > .bv-pill__label, +.stx-pill--is-minimized > .bv-pill__dismiss-button { + display: none; +} + +.stx-pill--is-filled { + background-color: #5C6670; + border-color: rgba(0,0,0,0.1); + color: #fff; +} + +.stx-pill--is-filled.bv-pill--with-info { + background-color: #3B63FF; + color: #fff; +} + +.stx-pill--is-filled.bv-pill--with-warning { + background-color: #B25600; + color: #fff; +} + +.stx-pill--is-filled.bv-pill--with-success { + background-color: #008757; + color: #fff; +} + +.stx-pill--is-filled.bv-pill--with-alert { + background-color: #E41244; + color: #fff; +} + +.stx-pill__label { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; +} + +.stx-pill__icon { + margin-left: -0.125rem; + margin-right: 0.125rem; +} + +.stx-pill__dismiss-button { + margin-left: 0.125rem; + margin-right: -0.125rem; + width: 1rem; + height: 1rem; +} diff --git a/src/components/pill/Pill.js b/src/components/pill/Pill.js new file mode 100644 index 0000000..ebe99a3 --- /dev/null +++ b/src/components/pill/Pill.js @@ -0,0 +1,75 @@ +import React from 'react'; +import cn from 'classnames'; +import PropTypes from 'prop-types'; +import times from './timesIcon'; + +import './Pill.css'; + +function Pill(props) { + const { + label, status, dismissible, minimized, filled, onClick, onDismiss, dismissType, dismissText, className, ...otherProps + } = props; + const classNames = cn([ + 'stx-pill', + { 'stx-pill--with-info': status === 'info' }, + { 'stx-pill--with-warning': status === 'warning' }, + { 'stx-pill--with-success': status === 'success' }, + { 'stx-pill--with-alert': status === 'alert' }, + { 'stx-pill--is-filled': filled }, + { 'stx-pill--is-minimized': minimized }, + { 'stx-pill--is-clickable': onClick }, + className, + ]); + + const timesIcon = + + + +; + + return ( + + + { label } + + { dismissible && } + + ); +} + +Pill.propTypes = { + /** Label for the pill */ + label: PropTypes.string, + /** Status modifier to change the color */ + status: PropTypes.oneOf(['alert', 'warning', 'success', 'info', '']), + /** Show the dismiss button */ + dismissible: PropTypes.bool, + /** Show the pill as a minimized indicator */ + minimized: PropTypes.bool, + /** Show the pill as a solid colour instead of an outline */ + filled: PropTypes.bool, + /** Event that fires when you click on the pill */ + onClick: PropTypes.func, + /** Event that fires when you click on the dismiss button */ + onDismiss: PropTypes.func, + /** Dismiss button type */ + dismissType: PropTypes.oneOf(['string', 'icon']), + /** Dismiss text for dismissType of string and aria label of dismiss button */ + dismissText: PropTypes.string, + /** Additional classname(s) */ + className: PropTypes.string, +}; + +export default Pill; diff --git a/src/components/pill/Pill.test.js b/src/components/pill/Pill.test.js new file mode 100644 index 0000000..ea40ff4 --- /dev/null +++ b/src/components/pill/Pill.test.js @@ -0,0 +1,104 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import Pill from './Pill'; + +describe('Pill', () => { + test('Pill renders with basic props', () => { + const component = shallow(); + + expect(toJson(component)).toMatchSnapshot(); + }); + + test('Pill renders with a11y props', () => { + const component = shallow(); + + const span = component.find('span.stx-pill'); + expect(span).toHaveLength(1); + expect(span.prop('aria-label')).toEqual('aria-label'); + expect(toJson(component)).toMatchSnapshot(); + }); + + test('Pill renders with dismiss button', () => { + const onDismissSpy = jest.fn(); + const component = shallow(); + + const dismissButton = component.find('button.stx-pill__dismiss-button'); + expect(dismissButton.exists()).toBe(true); + expect(dismissButton.prop('onClick')).toBe(onDismissSpy); + }); + + test('Pill renders with alert status', () => { + const component = shallow(); + + const span = component.find('span.stx-pill--with-alert'); + expect(span.exists()).toBe(true); + }); + + test('Pill renders with warning status', () => { + const component = shallow(); + + const span = component.find('span.stx-pill--with-warning'); + expect(span.exists()).toBe(true); + }); + + test('Pill renders with success status', () => { + const component = shallow(); + + const span = component.find('span.stx-pill--with-success'); + expect(span.exists()).toBe(true); + }); + + test('Pill renders with info status', () => { + const component = shallow(); + + const span = component.find('span.stx-pill--with-info'); + expect(span.exists()).toBe(true); + }); + + test('Pill renders as minimized', () => { + const component = shallow(); + + const span = component.find('span.stx-pill--is-minimized'); + expect(span.exists()).toBe(true); + }); + + test('Pill renders as filled', () => { + const component = shallow(); + + const span = component.find('span.stx-pill--is-filled'); + expect(span.exists()).toBe(true); + }); + + test('Pill renders as clickable', () => { + const onClickSpy = jest.fn(); + const component = shallow(); + + const span = component.find('span.stx-pill--is-clickable'); + expect(span.exists()).toBe(true); + expect(span.prop('onClick')).toBe(onClickSpy); + }); +}); diff --git a/src/components/pill/Readme.md b/src/components/pill/Readme.md new file mode 100644 index 0000000..abdd0ff --- /dev/null +++ b/src/components/pill/Readme.md @@ -0,0 +1,61 @@ +### Purpose +Give the user a brief (1 - 2 word) status message that can sit inline with other text or elements. + +### Examples +#### Standard +```jsx + + + + + + + +``` +#### Filled +```jsx + + + + + + + +``` + +#### Dismissibles +##### Default dismissType +```jsx + + myFunction() } + label="Info" + status="info" + /> + + + +``` + +##### Icon dismissType +```jsx + + myFunction() } + label="Warning" + status="warning" + /> + + + +``` + +#### Pills as status for something +These 5 status types can carry different meanings. If Pill is being used to show status, several different labels can be used with the same status type. For example, `warning` can refer to "In Progress" and "Incomplete" at the same time; basically anything that is proceeding with caution. + +`success` should be reserved for status that is both complete and positive, and `alert` should be reserve for status that is both complete and negative. diff --git a/src/components/pill/__snapshots__/Pill.test.js.snap b/src/components/pill/__snapshots__/Pill.test.js.snap new file mode 100644 index 0000000..b336fc6 --- /dev/null +++ b/src/components/pill/__snapshots__/Pill.test.js.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Pill Pill renders with a11y props 1`] = ` + + + +`; + +exports[`Pill Pill renders with basic props 1`] = ` + + + label + + +`; diff --git a/src/components/pill/timesIcon.js b/src/components/pill/timesIcon.js new file mode 100644 index 0000000..a9b1496 --- /dev/null +++ b/src/components/pill/timesIcon.js @@ -0,0 +1,2 @@ +const times = 'M11.5,10.3786797 L4.56066017,3.43933983 C3.97487373,2.85355339 3.02512627,2.85355339 2.43933983,3.43933983 C1.85355339,4.02512627 1.85355339,4.97487373 2.43933983,5.56066017 L9.37867966,12.5 L2.43933983,19.4393398 C1.85355339,20.0251263 1.85355339,20.9748737 2.43933983,21.5606602 C3.02512627,22.1464466 3.97487373,22.1464466 4.56066017,21.5606602 L11.5,14.6213203 L18.4393398,21.5606602 C19.0251263,22.1464466 19.9748737,22.1464466 20.5606602,21.5606602 C21.1464466,20.9748737 21.1464466,20.0251263 20.5606602,19.4393398 L13.6213203,12.5 L20.5606602,5.56066017 C21.1464466,4.97487373 21.1464466,4.02512627 20.5606602,3.43933983 C19.9748737,2.85355339 19.0251263,2.85355339 18.4393398,3.43933983 L11.5,10.3786797 Z'; +export default times; diff --git a/src/index.js b/src/index.js index 374354d..86cec32 100644 --- a/src/index.js +++ b/src/index.js @@ -19,6 +19,7 @@ export { default as CheckboxField } from './components/checkboxfield/CheckboxFie export { default as LoadingSpinner } from './components/loadingspinner/LoadingSpinner'; export { default as ProgressBar } from './components/progressbar/ProgressBar'; export { default as Message } from './components/message/Message'; +export { default as Pill } from './components/pill/Pill'; // Product export { default as Details } from './components/details/Details'; diff --git a/styleguide.config.js b/styleguide.config.js index 6eded6b..20f41f4 100644 --- a/styleguide.config.js +++ b/styleguide.config.js @@ -59,6 +59,7 @@ module.exports = { 'src/components/loadingspinner/LoadingSpinner.js', 'src/components/progressbar/ProgressBar.js', 'src/components/message/Message.js', + 'sec/components/pill/Pill.js', ], },