diff --git a/packages/cxl-lumo-styles/scss/global.scss b/packages/cxl-lumo-styles/scss/global.scss index c8f8c0db3..a80427924 100644 --- a/packages/cxl-lumo-styles/scss/global.scss +++ b/packages/cxl-lumo-styles/scss/global.scss @@ -6,6 +6,8 @@ html { --cxl-content-width: 36em; --cxl-wrap-width: 72rem; --cxl-wrap-padding: var(--lumo-space-m); + --cxl-color-light-gray: hsla(0, 0%, 96%, 1); + --cxl-color-black-50pct: hsla(0, 0%, 0%, 0.5); /** * Lumo Icons have a documented 4px "safe area" around them. Vaadin Icons don't, for unknown reasons. diff --git a/packages/cxl-ui/scss/cxl-stats.scss b/packages/cxl-ui/scss/cxl-stats.scss new file mode 100644 index 000000000..98e7ae826 --- /dev/null +++ b/packages/cxl-ui/scss/cxl-stats.scss @@ -0,0 +1,17 @@ +@use "~@conversionxl/cxl-lumo-styles/scss/mq"; + +:host { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--lumo-space-m); + padding: var(--lumo-space-xl) var(--lumo-space-m) var(--lumo-space-m); + border-bottom: 1px solid var(--cxl-color-light-gray); + + @media #{mq.$small} { + padding: var(--lumo-space-xl) var(--lumo-space-l) var(--lumo-space-l); + } + + @media #{mq.$medium} { + grid-template-columns: repeat(4, 1fr); + } +} diff --git a/packages/cxl-ui/scss/global/cxl-stats.scss b/packages/cxl-ui/scss/global/cxl-stats.scss new file mode 100644 index 000000000..7baa05f50 --- /dev/null +++ b/packages/cxl-ui/scss/global/cxl-stats.scss @@ -0,0 +1,16 @@ +cxl-stats { + .stats-title { + margin-top: 0; + font-size: var(--lumo-font-size-xs); + font-weight: 400; + line-height: 1; + color: var(--cxl-color-black-50pct); + text-transform: uppercase; + } + + .stats-count { + margin: 0; + font-size: var(--lumo-font-size-xxl); + font-weight: 700; + } +} diff --git a/packages/cxl-ui/src/components/cxl-stats.js b/packages/cxl-ui/src/components/cxl-stats.js new file mode 100644 index 000000000..324bfaef6 --- /dev/null +++ b/packages/cxl-ui/src/components/cxl-stats.js @@ -0,0 +1,28 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { LitElement, html } from 'lit'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { customElement } from 'lit/decorators.js'; +import { registerGlobalStyles } from '@conversionxl/cxl-lumo-styles/src/utils'; +import '@conversionxl/cxl-lumo-styles'; +import cxlStatsStyles from '../styles/cxl-stats-css.js'; +import cxlStatsGlobalStyles from '../styles/global/cxl-stats-css.js'; + +@customElement('cxl-stats') +export class CXLStatsElement extends LitElement { + static get styles() { + return [cxlStatsStyles]; + } + + render() { + return html``; + } + + firstUpdated(_changedProperties) { + super.firstUpdated(_changedProperties); + + // Global styles. + registerGlobalStyles(cxlStatsGlobalStyles, { + moduleId: 'cxl-stats', + }); + } +} diff --git a/packages/cxl-ui/src/index-core.js b/packages/cxl-ui/src/index-core.js index 6ce2a0833..25a1ec1ab 100644 --- a/packages/cxl-ui/src/index-core.js +++ b/packages/cxl-ui/src/index-core.js @@ -17,6 +17,7 @@ export { CXLCardElement } from './components/cxl-card.js'; export { CXLCheckoutDetailsElement } from './components/cxl-checkout-details.js'; export { CXLMarketingNavElement } from './components/cxl-marketing-nav.js'; export { CXLSectionElement } from './components/cxl-section.js'; +export { CXLStatsElement } from './components/cxl-stats.js'; export { CXLTabsSliderElement } from './components/cxl-tabs-slider.js'; export { CXLNotification } from './components/cxl-notification.js'; diff --git a/packages/cxl-ui/src/index-storybook.js b/packages/cxl-ui/src/index-storybook.js index 58042c9c5..2d17ec740 100644 --- a/packages/cxl-ui/src/index-storybook.js +++ b/packages/cxl-ui/src/index-storybook.js @@ -6,6 +6,7 @@ export { CXLAppLayoutElement } from './components/cxl-app-layout.js'; export { CXLCardElement } from './components/cxl-card.js'; export { CXLMarketingNavElement } from './components/cxl-marketing-nav.js'; export { CXLSectionElement } from './components/cxl-section.js'; +export { CXLStatsElement } from './components/cxl-stats.js'; export { CXLTabsSliderElement } from './components/cxl-tabs-slider.js'; // Order matters. diff --git a/packages/storybook/cxl-ui/cxl-stats/cxl-stats.data.json b/packages/storybook/cxl-ui/cxl-stats/cxl-stats.data.json new file mode 100644 index 000000000..54eaef5c9 --- /dev/null +++ b/packages/storybook/cxl-ui/cxl-stats/cxl-stats.data.json @@ -0,0 +1,18 @@ +[ + { + "title": "Categories
completed", + "count": "1" + }, + { + "title": "Courses
completed", + "count": "30" + }, + { + "title": "Lessons
completed", + "count": "95" + }, + { + "title": "Complete
library", + "count": "12%" + } +] diff --git a/packages/storybook/cxl-ui/cxl-stats/default.stories.js b/packages/storybook/cxl-ui/cxl-stats/default.stories.js new file mode 100644 index 000000000..06f94aba2 --- /dev/null +++ b/packages/storybook/cxl-ui/cxl-stats/default.stories.js @@ -0,0 +1,40 @@ +import { html } from 'lit'; +import { unsafeHTML } from 'lit/directives/unsafe-html.js'; +import '@conversionxl/cxl-lumo-styles'; +import '@conversionxl/cxl-ui/src/components/cxl-stats.js'; +import statsData from './cxl-stats.data.json'; + +export default { + title: 'CXL UI/cxl-stats', +}; + +export const CXLStats = ({ statsCount }) => { + for (let i = 0; i < statsCount; i += 1) { + const newItem = { + title: 'Complete
library', + count: '12%', + }; + + statsData.push(newItem); + } + + return html` + + ${statsData.slice(0, statsCount).map( + (el) => html` +
+

${unsafeHTML(el.title)}

+

${el.count}

+
+ ` + )} +
+ `; +}; + +Object.assign(CXLStats, { + args: { + statsCount: 4, + }, + storyName: 'CXL Stats', +});