From 812961a6236ccf97d720fa15d693b6aa9bb01b78 Mon Sep 17 00:00:00 2001 From: vicky-comeau <110498164+vicky-comeau@users.noreply.github.com> Date: Fri, 7 Jul 2023 14:05:06 -0400 Subject: [PATCH] Feat: [OV-43444] Metric initial release (#452) * Feat: [OV-43444] Metric initial release * Update packages/Metric/src/metric.scss - remove space Co-authored-by: Franck Gaudin * Update packages/Metric/src/metric.scss - remove space Co-authored-by: Franck Gaudin * Update packages/Metric/src/metric.scss - remove space Co-authored-by: Franck Gaudin --------- Co-authored-by: Franck Gaudin --- .changeset/spicy-insects-behave.md | 5 + packages/Metric/.npmignore | 1 + packages/Metric/README.md | 44 ++++ packages/Metric/jest.config.js | 8 + packages/Metric/package.json | 37 ++++ packages/Metric/rollup.config.js | 6 + packages/Metric/src/Metric.stories.tsx | 169 +++++++++++++++ packages/Metric/src/Metric.test.tsx | 194 +++++++++++++++++ packages/Metric/src/Metric.tsx | 117 ++++++++++ packages/Metric/src/Score.tsx | 79 +++++++ .../src/__snapshots__/Metric.test.tsx.snap | 87 ++++++++ packages/Metric/src/metric.scss | 205 ++++++++++++++++++ packages/Metric/src/score.scss | 76 +++++++ packages/Metric/tsconfig.json | 13 ++ website/example/Metric.demo.js | 26 +++ website/package.json | 1 + website/svg/components/metric.svg | 6 + 17 files changed, 1074 insertions(+) create mode 100644 .changeset/spicy-insects-behave.md create mode 100644 packages/Metric/.npmignore create mode 100644 packages/Metric/README.md create mode 100644 packages/Metric/jest.config.js create mode 100644 packages/Metric/package.json create mode 100644 packages/Metric/rollup.config.js create mode 100644 packages/Metric/src/Metric.stories.tsx create mode 100644 packages/Metric/src/Metric.test.tsx create mode 100644 packages/Metric/src/Metric.tsx create mode 100644 packages/Metric/src/Score.tsx create mode 100644 packages/Metric/src/__snapshots__/Metric.test.tsx.snap create mode 100644 packages/Metric/src/metric.scss create mode 100644 packages/Metric/src/score.scss create mode 100644 packages/Metric/tsconfig.json create mode 100644 website/example/Metric.demo.js create mode 100644 website/svg/components/metric.svg diff --git a/.changeset/spicy-insects-behave.md b/.changeset/spicy-insects-behave.md new file mode 100644 index 00000000..5261f601 --- /dev/null +++ b/.changeset/spicy-insects-behave.md @@ -0,0 +1,5 @@ +--- +'@igloo-ui/metric': minor +--- + +The initial release of the metric component diff --git a/packages/Metric/.npmignore b/packages/Metric/.npmignore new file mode 100644 index 00000000..aa8e45f1 --- /dev/null +++ b/packages/Metric/.npmignore @@ -0,0 +1 @@ +src/ \ No newline at end of file diff --git a/packages/Metric/README.md b/packages/Metric/README.md new file mode 100644 index 00000000..3c2899fb --- /dev/null +++ b/packages/Metric/README.md @@ -0,0 +1,44 @@ +# Metric + +A metric component is a button that visually represents a specific metric or measurement. It provides a concise and interactive way for users to access important information or track key performance indicators. + + + + + +## Installation + +To install `@igloo-ui/metric` in your project, you will need to run the following command using [npm](https://www.npmjs.com/): + +```bash +npm install @igloo-ui/metric +``` + +If you prefer [Yarn](https://classic.yarnpkg.com/en/), use the following command instead: + +```bash +yarn add @igloo-ui/metric +``` + +## Usage + +Then to use the component in your code just import it! + +```jsx +import Metric from '@igloo-ui/metric'; +import Wellness from '@igloo-ui/icons/dist/Wellness'; + +const [selected, setSelected] = React.useState(false); +const handleOnPress = () => { + setSelected(!selected); +}; + +} + onPress={handleOnPress} + appearance={selected ? 'selected' : 'positive'} +/>; +``` diff --git a/packages/Metric/jest.config.js b/packages/Metric/jest.config.js new file mode 100644 index 00000000..a07e34c9 --- /dev/null +++ b/packages/Metric/jest.config.js @@ -0,0 +1,8 @@ +const base = require('../../jest.config'); +const { name } = require('./package.json'); + +module.exports = { + ...base, + name, + displayName: name, +}; diff --git a/packages/Metric/package.json b/packages/Metric/package.json new file mode 100644 index 00000000..e79126c5 --- /dev/null +++ b/packages/Metric/package.json @@ -0,0 +1,37 @@ +{ + "name": "@igloo-ui/metric", + "version": "0.0.0", + "main": "dist/Metric.js", + "module": "dist/Metric.js", + "types": "dist/Metric.d.ts", + "styles": "dist/metric.css", + "publishConfig": { + "access": "public" + }, + "repository": "git+https://github.com/gsoft-inc/ov-igloo-ui.git", + "author": "Officevibe Inc.", + "contributors": [ + "Vicky Comeau vicky.comeau@gsoft.com" + ], + "license": "Apache-2.0", + "peerDependencies": { + "react": ">= 16.8.6" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "rollup -c rollup.config.js" + }, + "dependencies": { + "@igloo-ui/icons": "^1.6.0", + "@igloo-ui/tokens": "^2.0.0", + "classnames": "^2.3.2", + "react-aria": "^3.22.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "IE >= 11" + ] +} diff --git a/packages/Metric/rollup.config.js b/packages/Metric/rollup.config.js new file mode 100644 index 00000000..d7eede6f --- /dev/null +++ b/packages/Metric/rollup.config.js @@ -0,0 +1,6 @@ +import * as pkg from './package.json'; +import { createRollupConfig } from '../../rollup.config.js'; + +const { name } = pkg; + +export default [createRollupConfig(name)]; diff --git a/packages/Metric/src/Metric.stories.tsx b/packages/Metric/src/Metric.stories.tsx new file mode 100644 index 00000000..ddef661f --- /dev/null +++ b/packages/Metric/src/Metric.stories.tsx @@ -0,0 +1,169 @@ +import React from 'react'; + + import { Meta, StoryObj } from '@storybook/react'; + + import Wellness from '@igloo-ui/icons/dist/Wellness'; + import WellnessSolid from '@igloo-ui/icons/dist/WellnessSolid'; + + import Section from '@components/section'; + import readme from '../README.md'; + + import Metric from './Metric'; + + export default { + title: 'Components/Metric', + component: Metric, + parameters: { + docs: { + description: { + component: readme, + } + } + }, + argTypes: { + icon: { control: { type: null } }, + } + } as Meta; + + type Story = StoryObj; + + export const Overview: Story = { + render: (props) => { + const [selected, setSelected] = React.useState(false); + const handleOnPress = () => { + setSelected(!selected); + }; + + return ( + + ); + }, + args: { + value: 20, + variation: 3, + label: 'Metric name', + icon: , + }, + }; + + export const Tooltip: Story = { + args: { + value: 20, + variation: 3, + label: 'Metric name', + appearance: 'positive', + icon: , + tooltip: 'This is a tooltip', + }, + }; + + export const Empty: Story = { + render: () => { + return ( +
+ } + /> + } + /> +
+ ); + } + }; + + export const Score: Story = { + render: () => { + return ( +
+ } + type="score" + /> + } + type="score" + /> + } + type="score" + /> +
+ ); + }, + }; + + export const SubMetric: Story = { + render: () => { + return ( +
+ + + +
+ ); + }, + }; + + export const Fluctuate: Story = { + render: () => { + return ( +
+ + +
+ ); + }, + }; diff --git a/packages/Metric/src/Metric.test.tsx b/packages/Metric/src/Metric.test.tsx new file mode 100644 index 00000000..7a1ee0b0 --- /dev/null +++ b/packages/Metric/src/Metric.test.tsx @@ -0,0 +1,194 @@ +/** + * @jest-environment jsdom + */ + import React from 'react'; + import { fireEvent, render, screen } from '@testing-library/react'; + import MockTooltip from '@igloo-ui/tooltip/src/__mocks__/Tooltip.mock'; + + import Metric, {MetricProps} from './Metric'; + + jest.mock('@igloo-ui/tooltip', () => ({ + __esModule: true, + default: jest.fn(MockTooltip), +})); + +const mockPropsEmpty: MetricProps = { + className: 'test-class', + label: 'Test Metric', + tooltip: 'Tooltip text', + type: 'score', +}; + +const mockPropsEmptyFluctuate: MetricProps = { + className: 'test-class', + label: 'Test Metric', + tooltip: 'Tooltip text', + type: 'fluctuate', +}; + + const mockPropsPositive: MetricProps = { + appearance: 'positive', + className: 'test-class', + icon:
Icon
, + label: 'Test Metric', + tooltip: 'Tooltip text', + type: 'score', + value: 10, + variation: 1, +}; + +const mockPropsNegative: MetricProps = { + appearance: 'negative', + className: 'test-class', + icon:
Icon
, + label: 'Test Metric', + tooltip: 'Tooltip text', + type: 'score', + value: 10, + variation: -6, +}; + +const mockPropsSelected: MetricProps = { + appearance: 'selected', + className: 'test-class', + icon:
Icon
, + label: 'Test Metric', + tooltip: 'Tooltip text', + type: 'score', + value: 10, + variation: -5, +}; + +const mockPropsSubMetric: MetricProps = { + className: 'test-class', + label: 'Test Metric', + tooltip: 'Tooltip text', + type: 'subMetric', + value: 10, + variation: 5, +}; + + const setup = (props: MetricProps = mockPropsPositive) => { + return render( + + ); +}; + + describe('Metric', () => { + test('should render without errors', () => { + setup(); + const wrapper = screen.getByTestId('ids-metric'); + expect(wrapper).toBeInTheDocument(); + expect(wrapper).toHaveClass('ids-metric'); + }); + + test('should render a snapshot', () => { + const {asFragment} = setup(); + expect(asFragment()).toMatchSnapshot(); + }); + + test('should render the label', () => { + setup(); + + const labelElement = screen.getByText('Test Metric'); + expect(labelElement).toBeInTheDocument(); + }); + + test('should render the custom label', () => { + setup({ ...mockPropsPositive, label: 'Custom Label' }); + + const labelElement = screen.getByText('Custom Label'); + expect(labelElement).toBeInTheDocument(); + }); + + test('should render the tooltip', () => { + const {container} = setup(); + + const trigger = container.querySelector('.ids-metric__status--tooltip .ids-tooltip__container'); + expect(trigger).toBeInTheDocument(); + if (trigger) { + fireEvent.click(trigger); + } + const tooltipElement = screen.getByText('Tooltip text'); + expect(tooltipElement).toBeInTheDocument(); + }); + + /* Score */ + + test('should render the value', () => { + setup(); + + const valueElement = screen.getByText('10.0'); + expect(valueElement).toBeInTheDocument(); + expect(valueElement).toHaveClass('ids-metric__score'); + }); + + /* Variation */ + + test('should render the variation', () => { + setup(); + + const variationElement = screen.getByText('1'); + const postfix = variationElement.querySelector('.ids-score__postfix'); + expect(postfix).toHaveTextContent('pt'); + expect(variationElement).toBeInTheDocument(); + expect(variationElement).toHaveClass('ids-score--variation'); + expect(variationElement).toContainElement(variationElement.querySelector('.ids-score__arrow--positive')); + }); + + test('should render a negative variation', () => { + setup(mockPropsNegative); + + const variationElement = screen.getByText('6'); + expect(variationElement).toBeInTheDocument(); + expect(variationElement).toHaveClass('ids-score--variation'); + expect(variationElement).toHaveClass('ids-score--negative'); + expect(variationElement).toContainElement(variationElement.querySelector('.ids-score__arrow--negative')); + }); + + /* Empty */ + + test('should not render a variation when empty', () => { + const { container } = setup(mockPropsEmpty); + + const variationElement = screen.getByText('0.0'); + expect(variationElement).toBeInTheDocument(); + const variationContainer = container.querySelector('.ids-score--variation'); + expect(variationContainer).not.toBeInTheDocument(); + }); + + test('when type fluctuate, it should render the variation with a decimal and pts', () => { + setup(mockPropsEmptyFluctuate); + + const variationElement = screen.getByText('0.0'); + expect(variationElement).toBeInTheDocument(); + expect(variationElement).toHaveClass('ids-score--variation'); + const arrow = variationElement.querySelector('.ids-score__arrow'); + expect(arrow).not.toBeInTheDocument(); + const postfix = screen.getByText('pts'); + expect(postfix).toBeInTheDocument(); + }); + + /* Selected */ + + test('should render the selected appearance', () => { + setup(mockPropsSelected); + + const wrapper = screen.getByTestId('ids-metric'); + expect(wrapper).toHaveClass('ids-metric--selected'); + const status = wrapper.querySelector('.ids-metric__status'); + expect(status).toBeInTheDocument(); + expect(status).not.toHaveClass('ids-metric__status--tooltip'); + expect(status).toContainElement(wrapper.querySelector('.ids-metric__checkmark')); + }); + + /* Sub Metric */ + + test('should render the subMetric type', () => { + setup(mockPropsSubMetric); + + const wrapper = screen.getByTestId('ids-metric'); + expect(wrapper).toHaveClass('ids-metric--subMetric'); + }); + +}); diff --git a/packages/Metric/src/Metric.tsx b/packages/Metric/src/Metric.tsx new file mode 100644 index 00000000..89281028 --- /dev/null +++ b/packages/Metric/src/Metric.tsx @@ -0,0 +1,117 @@ +import * as React from 'react'; +import cx from 'classnames'; +import { useButton, AriaButtonProps, FocusRing } from 'react-aria'; + +import HelpSolid from '@igloo-ui/icons/dist/HelpSolid'; +import Alignment from '@igloo-ui/icons/dist/Alignment'; +import Checkmark from '@igloo-ui/icons/dist/Checkmark'; + +import Tooltip from '@igloo-ui/tooltip'; +import Score from './Score'; + +import './metric.scss'; + +export type Appearance = 'positive' | 'negative' | 'selected'; +export type MetricType = 'score' | 'fluctuate' | 'subMetric'; + +export interface MetricProps extends Omit { + /** The appearance of the metric */ + appearance?: Appearance; + /** Add a specific class to the metric */ + className?: string; + /** Add a data-test tag for automated tests */ + dataTest?: string; + /** The icon to display */ + icon?: React.ReactElement; + /** The label or metric name to display */ + label: string; + /** The tooltip to display */ + tooltip?: string; + /** The type of metric */ + type?: MetricType; + /** The value of the metric (Only variation is used for type fluctuate) */ + value?: number; + /** The variation of the metric */ + variation?: number; +} + +const Metric: React.FunctionComponent = ({ + appearance, + className, + dataTest, + icon, + label, + tooltip, + type = 'score', + value, + variation, + ...rest +}: MetricProps) => { + const btnRef = React.useRef(null); + + const { buttonProps } = useButton(rest, btnRef); + + const classes = cx('ids-metric', className, { + [`ids-metric--${appearance}`]: appearance, + [`ids-metric--${type}`]: type, + }); + const visualClasses = cx('ids-metric__visual', { + [`ids-metric__visual--${appearance}`]: appearance, + }); + + return ( + + + + ); +}; + +export default Metric; diff --git a/packages/Metric/src/Score.tsx b/packages/Metric/src/Score.tsx new file mode 100644 index 00000000..c0243ab3 --- /dev/null +++ b/packages/Metric/src/Score.tsx @@ -0,0 +1,79 @@ +import * as React from 'react'; +import cx from 'classnames'; + +import ArrowUp from '@igloo-ui/icons/dist/ArrowUp'; +import ArrowDown from '@igloo-ui/icons/dist/ArrowDown'; + +import './score.scss'; + +export interface ScoreProps extends React.ComponentProps<'div'> { + /** The size of the arrow */ + arrowSize?: 'small' | 'medium' | 'large'; + /** Add a specific class to the metric */ + className?: string; + /** Force the score to have a decimal */ + forceDecimal?: boolean; + /** Whether or not the score should be hidden if it is zero */ + hideIfZero?: boolean; + /** Whether or not the score should have a selected style */ + isSelected?: boolean; + /** Whether or not the score is a variation */ + isVariation?: boolean; + /** The value of the score */ + value?: number; +} + +const Score: React.FunctionComponent = ({ + arrowSize = 'small', + className, + forceDecimal = false, + hideIfZero = false, + isSelected = false, + isVariation = false, + value = 0, +}: ScoreProps) => { + const isNegative = value < 0; + const absoluteValue = Math.abs(value); + const isZero = value === 0; + const metricValue = isVariation ? Math.abs(value) : value; + const hideValue = isZero && hideIfZero; + let displayValue = metricValue.toString(); + if (forceDecimal) { + displayValue = metricValue.toFixed(1); + } + const arrow = isNegative ? ( + + ) : ( + + ); + const postFix = absoluteValue === 1 ? ' pt' : ' pts'; + + if (!hideValue) { + return ( + + {isVariation && !isZero && arrow} + {displayValue} + {isVariation && {postFix}} + + ); + } + return null; +}; + +export default Score; diff --git a/packages/Metric/src/__snapshots__/Metric.test.tsx.snap b/packages/Metric/src/__snapshots__/Metric.test.tsx.snap new file mode 100644 index 00000000..8bfc9596 --- /dev/null +++ b/packages/Metric/src/__snapshots__/Metric.test.tsx.snap @@ -0,0 +1,87 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Metric should render a snapshot 1`] = ` + + + +`; diff --git a/packages/Metric/src/metric.scss b/packages/Metric/src/metric.scss new file mode 100644 index 00000000..6250cf6d --- /dev/null +++ b/packages/Metric/src/metric.scss @@ -0,0 +1,205 @@ +@use '~@igloo-ui/tokens/dist/base10/variables' as tokens; +@use '~@igloo-ui/tokens/dist/fonts'; + +:root { + /* Default */ + --ids-metric-background: #{tokens.$samoyed}; + --ids-metric-font-family: #{tokens.$primary-font-family}; + --ids-metric-font-size: #{tokens.$font-size-3}; + --ids-metric-padding: #{tokens.$space-2}; + --ids-metric-border-radius: #{tokens.$border-radius-sm}; + --ids-metric-line-height: #{tokens.$line-height-xxl}; + --ids-metric-focus-border-color: #{tokens.$electric-blue-600}; + + /* Visual */ + --ids-metric-visual-background: #{tokens.$grey-400}; + --ids-metric-visual-border-radius: 4rem; + --ids-metric-visual-color: #{tokens.$grey-600}; + --ids-metric-visual-size: #{tokens.$space-6}; + --ids-metric-visual-positive-background: #{tokens.$seaweed-50}; + --ids-metric-visual-positive-color: #{tokens.$seaweed-500}; + --ids-metric-visual-negative-background: #{tokens.$coral-100}; + --ids-metric-visual-negative-color: #{tokens.$coral-500}; + --ids-metric-visual-selected-background: #{tokens.$electric-blue-100}; + --ids-metric-visual-selected-color: #{tokens.$electric-blue-500}; + --ids-metric-visual-margin: 0 #{tokens.$space-3} 0 0; + + /* Hover */ + --ids-metric-hover-shadow: #{tokens.$shadow-12}; + --ids-metric-hover-background: #{tokens.$electric-blue-50}; + --ids-metric-hover-border-color: #{tokens.$electric-blue-500}; + + /* Score */ + --ids-metric-score-margin-left: #{tokens.$space-2}; + + /* Name */ + --ids-metric-name-color: #{tokens.$grey-600}; + + /* Status */ + --ids-metric-status-color: #{tokens.$grey-500}; + --ids-metric-status-selected-color: #{tokens.$electric-blue-500}; + + /* Fluctuate */ + --ids-metric-alignment-icon-margin: #{tokens.$space-2}; + + /* Sub Metric */ + --ids-metric-sub-metric-border-color: #{tokens.$grey-300}; + --ids-metric-sub-metric-padding-left: #{tokens.$space-3}; + --ids-metric-sub-metric-focus-shadow: #{tokens.$focus}; + + /* Sub Metric Selected */ + --ids-metric-sub-metric-selected-border-color: #{tokens.$grey-400}; + --ids-metric-sub-metric-selected-left-border-color: #{tokens.$electric-blue-500}; + --ids-metric-sub-metric-selected-left-border-width: #{tokens.$space-1}; +} + +.ids-metric { + align-items: center; + background: var(--ids-metric-background); + border: 1.5px solid transparent; + border-radius: var(--ids-metric-border-radius); + display: flex; + font-family: var(--ids-metric-font-family); + font-size: var(--ids-metric-font-size); + outline: none; + overflow: hidden; + padding: var(--ids-metric-padding); + position: relative; + text-align: left; + transition: background 0.2s ease-in-out, border 0.2s ease-in-out; + width: 100%; + + &, + & * { + box-sizing: border-box; + } + + &:hover { + box-shadow: var(--ids-metric-hover-shadow); + cursor: pointer; + } + + &.keyboard-focus { + outline: none; + border-color: var(--ids-metric-focus-border-color); + box-shadow: var(--ids-metric-hover-shadow); + } +} + +.ids-metric--selected { + box-shadow: none; + + &:hover { + background: var(--ids-metric-hover-background); + box-shadow: none; + } + + &.keyboard-focus { + background: var(--ids-metric-hover-background); + box-shadow: none; + } +} + +.ids-metric--subMetric { + border: 1px solid var(--ids-metric-sub-metric-border-color); + padding-left: var(--ids-metric-sub-metric-padding-left); + + &:hover { + background: var(--ids-metric-hover-background); + border-color: var(--ids-metric-hover-border-color); + box-shadow: none; + } + + &.keyboard-focus { + background: var(--ids-metric-hover-background); + border-color: var(--ids-metric-hover-border-color); + box-shadow: var(--ids-metric-sub-metric-focus-shadow); + } + + &.ids-metric--selected { + border-color: var( --ids-metric-sub-metric-selected-border-color); + + &:hover { + border-color: var(--ids-metric-hover-border-color); + } + + &::before { + background: var(--ids-metric-sub-metric-selected-left-border-color); + bottom: 0; + content: ''; + left: 0; + position: absolute; + top: 0; + width: var(--ids-metric-sub-metric-selected-left-border-width); + } + } +} + +.ids-metric__visual { + align-items: center; + background: var(--ids-metric-visual-background); + border-radius: var(--ids-metric-visual-border-radius); + color: var(--ids-metric-visual-color); + display: flex; + flex: 0 0 auto; + height: var(--ids-metric-visual-size); + justify-content: center; + margin: var(--ids-metric-visual-margin); + width: var(--ids-metric-visual-size); + + &--positive { + background: var(--ids-metric-visual-positive-background); + color: var(--ids-metric-visual-positive-color); + } + + &--negative { + background: var(--ids-metric-visual-negative-background); + color: var(--ids-metric-visual-negative-color); + } + + &--selected { + background: var(--ids-metric-visual-selected-background); + color: var(--ids-metric-visual-selected-color); + } +} + +.ids-metric__content { + flex: 1 1 auto; +} + +.ids-metric__score-group { + align-items: baseline; + display: flex; + + & .ids-metric__score ~ .ids-metric__score { + margin-left: var(--ids-metric-score-margin-left); + } +} + +.ids-metric__name { + align-items: center; + color: var(--ids-metric-name-color); + display: flex; + line-height: var(--ids-metric-line-height); +} + +.ids-metric__status { + color: var(--ids-metric-status-selected-color); + flex: 0 0 auto; + transition: opacity 0.2s ease-in-out; + + &--tooltip { + color: var(--ids-metric-status-color); + opacity: 0; + } + + .ids-metric:hover &--tooltip { + opacity: 1; + } +} + +.ids-metric__alignment-icon { + flex: 0 0 auto; + margin-right: var(--ids-metric-alignment-icon-margin); +} + \ No newline at end of file diff --git a/packages/Metric/src/score.scss b/packages/Metric/src/score.scss new file mode 100644 index 00000000..496ac355 --- /dev/null +++ b/packages/Metric/src/score.scss @@ -0,0 +1,76 @@ +@use '~@igloo-ui/tokens/dist/base10/variables' as tokens; +@use '~@igloo-ui/tokens/dist/fonts'; + +:root { + /* Default */ + --ids-score-font-family: #{tokens.$secondary-font-family}; + --ids-score-font-size: #{tokens.$font-size-5}; + --ids-score-font-weight: #{tokens.$font-weight-medium}; + --ids-score-color: #{tokens.$grey-800}; + --ids-score-line-height: #{tokens.$line-height-xl}; + + /* Variation */ + --ids-score-variation-font-family: #{tokens.$primary-font-family}; + --ids-score-variation-font-size: #{tokens.$font-size-3}; + --ids-score-variation-font-weight: #{tokens.$font-weight-regular}; + --ids-score-variation-line-height: #{tokens.$line-height-xxl}; + --ids-score-single-variation-font-size: #{tokens.$font-size-9}; + --ids-score-single-variation-color: #{tokens.$grey-600}; + --ids-score-single-variation-line-height: #{tokens.$line-height-xxxs}; + + /* Arrows */ + --ids-score-arrow-color-positive: #{tokens.$seaweed-500}; + --ids-score-arrow-color-negative: #{tokens.$coral-500}; + --ids-score-arrow-color-selected: #{tokens.$electric-blue-500}; + --ids-score-arrow-margin-right: #{tokens.$space-1}; +} + +.ids-score { + color: var(--ids-score-color); + font-family: var(--ids-score-font-family); + font-size: var(--ids-score-font-size); + font-weight: var(--ids-score-font-weight); + line-height: var(--ids-score-line-height); + + &--variation { + align-items: baseline; + color: var(--ids-score-single-variation-color); + display: flex; + font-family: var(--ids-score-variation-font-family); + font-size: var(--ids-score-single-variation-font-size); + font-weight: var(--ids-score-variation-font-weight); + line-height: var(--ids-score-single-variation-line-height); + } + + .ids-score ~ &--variation { + color: var(--ids-score-color); + font-size: var(--ids-score-variation-font-size); + font-weight: var(--ids-score-variation-font-weight); + line-height: var(--ids-score-variation-line-height); + } +} + +.ids-score__arrow { + align-self: center; + flex: 0 0 auto; + font-size: var(--ids-score-font-size); + margin-right: var(--ids-score-arrow-margin-right); + + &--positive { + color: var( --ids-score-arrow-color-positive); + } + + &--negative { + color: var( --ids-score-arrow-color-negative); + } + + &--selected { + color: var( --ids-score-arrow-color-selected); + } +} + +.ids-score__postfix { + font-size: var(--ids-score-variation-font-size); + line-height: var(--ids-score-variation-line-height); +} + \ No newline at end of file diff --git a/packages/Metric/tsconfig.json b/packages/Metric/tsconfig.json new file mode 100644 index 00000000..e250e642 --- /dev/null +++ b/packages/Metric/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.build.json", + "include": ["src/**/*", "../../typings/**/*", "rollup.config.js"], + "exclude": ["dist"], + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "declarationDir": "./dist", + "composite": true, + "baseUrl": ".", + "typeRoots": ["../../typings", "../../node_modules/@types"] + } +} diff --git a/website/example/Metric.demo.js b/website/example/Metric.demo.js new file mode 100644 index 00000000..ed2fbf85 --- /dev/null +++ b/website/example/Metric.demo.js @@ -0,0 +1,26 @@ +import React from 'react'; +import Metric from '@igloo-ui/metric'; +import Wellness from '@igloo-ui/icons/dist/Wellness'; + +const Example = () => { + const [selected, setSelected] = React.useState(false); + const handleOnPress = () => { + setSelected(!selected); + } + + return ( +
+ } + onPress={handleOnPress} + appearance={selected ? 'selected' : 'positive'} + /> +
+ ); +}; + +export default Example; diff --git a/website/package.json b/website/package.json index c1514acc..30e554f4 100644 --- a/website/package.json +++ b/website/package.json @@ -35,6 +35,7 @@ "@igloo-ui/icon-button": "*", "@igloo-ui/input": "*", "@igloo-ui/list": "*", + "@igloo-ui/metric": "*", "@igloo-ui/modal": "*", "@igloo-ui/option-button": "*", "@igloo-ui/pager": "*", diff --git a/website/svg/components/metric.svg b/website/svg/components/metric.svg new file mode 100644 index 00000000..a12d4091 --- /dev/null +++ b/website/svg/components/metric.svg @@ -0,0 +1,6 @@ + + + + + +