Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(meter): create new component #695

Merged
merged 27 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c94f147
feat(meter): initial structure
gabrielduete Jan 31, 2025
ac17a0f
feat(meter): initial structure stories
gabrielduete Jan 31, 2025
54da4a9
feat(meter): update props type and size
gabrielduete Feb 3, 2025
724ba69
feat(meter): apdate props type and size in stories
gabrielduete Feb 3, 2025
36e3948
feat(meter): add calc progress and children
gabrielduete Feb 3, 2025
8fa1df4
feat(meter): add centertitle prop to component
gabrielduete Feb 4, 2025
7cf0206
feat(meter): add centertitle prop to component and update margin styles
gabrielduete Feb 4, 2025
c63a61d
feat(meter): update stories react and vue
gabrielduete Feb 4, 2025
af5484b
test(meter): create tests
gabrielduete Feb 4, 2025
eca136d
Merge branch 'main' into feat/create-component-meter
gabrielduete Feb 5, 2025
4bd33fa
feat(meter): update font mobile
gabrielduete Feb 5, 2025
1f32007
Merge branch 'feat/create-component-meter' of github.com:juntossomosm…
gabrielduete Feb 5, 2025
9ac14c4
feat(meter): add func ismobile in utils
gabrielduete Feb 5, 2025
be97b7d
feat(meter): turns prop min without default value
gabrielduete Feb 5, 2025
baaf5db
refactor(meter): update class name for center title
gabrielduete Feb 5, 2025
77a5891
refactor(meter): update class name for margin top
gabrielduete Feb 5, 2025
9c60994
refactor(meter): update prop name center title
gabrielduete Feb 5, 2025
073697f
test(meter): adjust test to new name prop center title
gabrielduete Feb 5, 2025
b6d3528
refactor(meter): remove unnecessary font weight
gabrielduete Feb 5, 2025
407764e
refactor(meter): remove unnecessary property
gabrielduete Feb 6, 2025
ce3e6d7
feat(meter): use breakpoints instead function
gabrielduete Feb 6, 2025
1465f8a
test(meter): add test mobile case
gabrielduete Feb 6, 2025
8ff4b79
refactor(meter): use sugar syntax breakpoints
gabrielduete Feb 6, 2025
7a1570d
refactor(meter): change name class step
gabrielduete Feb 6, 2025
3fb2198
test(meter): adjust class step in tests
gabrielduete Feb 6, 2025
7baa27f
refactor(meter): reorganize stories to new prop name center title
gabrielduete Feb 6, 2025
04727a6
refactor(meter): adjust name class step
gabrielduete Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,15 @@ export namespace Components {
}
interface AtomListSliderItem {
}
interface AtomMeter {
"actual": number;
"hasCenterTitle"?: boolean;
"max": number;
"min": number;
"size"?: 'small' | 'large';
"title": string;
"type"?: 'success' | 'neutral' | 'warning' | 'danger';
}
interface AtomModal {
"alertType"?: 'alert' | 'error';
"disablePrimaryButton": boolean;
Expand Down Expand Up @@ -572,6 +581,12 @@ declare global {
prototype: HTMLAtomListSliderItemElement;
new (): HTMLAtomListSliderItemElement;
};
interface HTMLAtomMeterElement extends Components.AtomMeter, HTMLStencilElement {
}
var HTMLAtomMeterElement: {
prototype: HTMLAtomMeterElement;
new (): HTMLAtomMeterElement;
};
interface HTMLAtomModalElementEventMap {
"atomCloseClick": any;
"atomDidDismiss": any;
Expand Down Expand Up @@ -724,6 +739,7 @@ declare global {
"atom-link": HTMLAtomLinkElement;
"atom-list-slider": HTMLAtomListSliderElement;
"atom-list-slider-item": HTMLAtomListSliderItemElement;
"atom-meter": HTMLAtomMeterElement;
"atom-modal": HTMLAtomModalElement;
"atom-pagination": HTMLAtomPaginationElement;
"atom-select": HTMLAtomSelectElement;
Expand Down Expand Up @@ -933,6 +949,15 @@ declare namespace LocalJSX {
}
interface AtomListSliderItem {
}
interface AtomMeter {
"actual"?: number;
"hasCenterTitle"?: boolean;
"max"?: number;
"min"?: number;
"size"?: 'small' | 'large';
"title"?: string;
"type"?: 'success' | 'neutral' | 'warning' | 'danger';
}
interface AtomModal {
"alertType"?: 'alert' | 'error';
"disablePrimaryButton"?: boolean;
Expand Down Expand Up @@ -1111,6 +1136,7 @@ declare namespace LocalJSX {
"atom-link": AtomLink;
"atom-list-slider": AtomListSlider;
"atom-list-slider-item": AtomListSliderItem;
"atom-meter": AtomMeter;
"atom-modal": AtomModal;
"atom-pagination": AtomPagination;
"atom-select": AtomSelect;
Expand Down Expand Up @@ -1142,6 +1168,7 @@ declare module "@stencil/core" {
"atom-link": LocalJSX.AtomLink & JSXBase.HTMLAttributes<HTMLAtomLinkElement>;
"atom-list-slider": LocalJSX.AtomListSlider & JSXBase.HTMLAttributes<HTMLAtomListSliderElement>;
"atom-list-slider-item": LocalJSX.AtomListSliderItem & JSXBase.HTMLAttributes<HTMLAtomListSliderItemElement>;
"atom-meter": LocalJSX.AtomMeter & JSXBase.HTMLAttributes<HTMLAtomMeterElement>;
"atom-modal": LocalJSX.AtomModal & JSXBase.HTMLAttributes<HTMLAtomModalElement>;
"atom-pagination": LocalJSX.AtomPagination & JSXBase.HTMLAttributes<HTMLAtomPaginationElement>;
"atom-select": LocalJSX.AtomSelect & JSXBase.HTMLAttributes<HTMLAtomSelectElement>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Meta, StoryObj } from '@storybook/vue3'

import { AtomBadge } from '@juntossomosmais/atomium/vue'
import { Meta, StoryObj } from '@storybook/vue3'

import { BadgeStoryArgs } from './badge.args'

Expand Down
79 changes: 79 additions & 0 deletions packages/core/src/components/meter/meter.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
:host {
align-items: flex-start;
display: flex;
flex-direction: column;
justify-content: flex-start;
width: 100%;
word-wrap: break-word;
}

.container-text {
align-items: flex-end;
display: flex;
font: var(--text-body-small);
justify-content: space-between;
letter-spacing: var(--text-body-small-letter);
margin-bottom: var(--spacing-xsmall);
width: 100%;
felipefialho marked this conversation as resolved.
Show resolved Hide resolved

&.has-center-title {
justify-content: center;
}

&.has-gap-top {
margin-bottom: 0;
margin-top: var(--spacing-xsmall);
}

@include below(small) {
font: var(--text-body-small);
letter-spacing: var(--text-body-small-letter);
}
}

.title {
font: var(--text-body-medium);
letter-spacing: var(--text-body-medium-letter);
margin: 0;

@include below(small) {
font: var(--text-body-small);
letter-spacing: var(--text-body-small-letter);
}
}

.atom-meter {
background-color: var(--color-neutral-light-4);
border-radius: var(--border-radius-medium);
height: var(--spacing-base);
position: relative;
width: 100%;

&.is-small {
height: var(--spacing-xsmall);

.within {
gabrielduete marked this conversation as resolved.
Show resolved Hide resolved
height: var(--spacing-xsmall);
}
}

.step {
background-color: var(--color-brand-secondary-regular);
border-radius: var(--border-radius-medium);
height: var(--spacing-base);
position: absolute;
width: 0;

&.is-success {
background-color: var(--color-contextual-success-light-1);
}

&.is-warning {
background-color: var(--color-contextual-warning-regular);
}

&.is-danger {
background-color: var(--color-contextual-error-regular);
}
}
}
136 changes: 136 additions & 0 deletions packages/core/src/components/meter/meter.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { newSpecPage } from '@stencil/core/testing'

import * as screens from '../../utils/screens'

import { AtomMeter } from './meter'

describe('atom-meter', () => {
it('should render with default props', async () => {
const page = await newSpecPage({
components: [AtomMeter],
html: `<atom-meter
type="neutral"
size="large"
title=":fialho-dino:"
min=1
max=6
actual=4
></atom-meter>`,
})

await page.waitForChanges()

expect(page.root).toEqualHtml(`
<atom-meter type="neutral" size="large" actual="4" max="6" min="1" title=":fialho-dino:">
gabrielduete marked this conversation as resolved.
Show resolved Hide resolved
<mock:shadow-root>
<div class="container-text">
<h1 class="title">
:fialho-dino:
</h1>
<slot></slot>
</div>
<div class="atom-meter is-large">
<div class="step is-neutral" style="width: 60%;"></div>
</div>
</mock:shadow-root>
</atom-meter>
`)
})

it('should render with centerTitle prop', async () => {
const page = await newSpecPage({
components: [AtomMeter],
html: `<atom-meter has-center-title="true" title="Centered Title" min="1" max="6" actual="4"></atom-meter>`,
})

await page.waitForChanges()

expect(page.root).toEqualHtml(`
<atom-meter has-center-title="true" title="Centered Title" min="1" max="6" actual="4">
<mock:shadow-root>
<div class="container-text has-center-title">
<h1 class="title">Centered Title</h1>
</div>
<div class="atom-meter is-large">
<div class="step is-neutral" style="width: 60%;"></div>
</div>
</mock:shadow-root>
</atom-meter>
`)
})

it('should render with size small prop', async () => {
const page = await newSpecPage({
components: [AtomMeter],
html: `<atom-meter title=":wessel-quase-coringa:" min="1" max="6" actual="4" size="small"></atom-meter>`,
})

await page.waitForChanges()

expect(page.root).toEqualHtml(`
<atom-meter title=":wessel-quase-coringa:" min="1" max="6" actual="4" size="small">
<mock:shadow-root>
<div class="container-text">
<h1 class="title">:wessel-quase-coringa:</h1>
<slot></slot>
</div>
<div class="atom-meter is-small">
<div class="step is-neutral" style="width: 60%;"></div>
</div>
</mock:shadow-root>
</atom-meter>
`)
})

it('should render with children', async () => {
const page = await newSpecPage({
components: [AtomMeter],
html: `<atom-meter title="test" min="1" max="6" actual="4">Children</atom-meter>`,
})

await page.waitForChanges()

expect(page.root).toEqualHtml(`
<atom-meter title="test" min="1" max="6" actual="4">
<mock:shadow-root>
<div class="container-text">
<h1 class="title">test</h1>
<slot></slot>
</div>
<div class="atom-meter is-large">
<div class="step is-neutral" style="width: 60%;"></div>
</div>
</mock:shadow-root>
Children
</atom-meter>
`)
})

it('should render mobile children', async () => {
jest.spyOn(screens, 'isMobile').mockReturnValue(true)

const page = await newSpecPage({
components: [AtomMeter],
html: `<atom-meter title="test" min="1" max="6" actual="4">Children</atom-meter>`,
})

await page.waitForChanges()

expect(page.root).toEqualHtml(`
<atom-meter title="test" min="1" max="6" actual="4">
<mock:shadow-root>
<div class="container-text">
<h1 class="title">test</h1>
</div>
<div class="atom-meter is-large">
<div class="step is-neutral" style="width: 60%;"></div>
</div>
<div class="container-text has-gap-top">
<slot></slot>
</div>
</mock:shadow-root>
Children
</atom-meter>
`)
})
})
tassioFront marked this conversation as resolved.
Show resolved Hide resolved
50 changes: 50 additions & 0 deletions packages/core/src/components/meter/meter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Component, h, Host, Prop } from '@stencil/core'

import { isMobile } from '../../utils/screens'

@Component({
tag: 'atom-meter',
styleUrl: 'meter.scss',
shadow: true,
})
export class AtomMeter {
@Prop() type?: 'success' | 'neutral' | 'warning' | 'danger' = 'neutral'
@Prop() size?: 'small' | 'large' = 'large'
@Prop() title: string
@Prop() hasCenterTitle?: boolean = false
@Prop() min: number
@Prop() max: number
@Prop() actual: number

private getProgress = () => {
if (this.actual >= this.max) return 100

if (this.actual <= this.min) return 0

return ((this.actual - this.min) / (this.max - this.min)) * 100
}

render() {
return (
<Host>
<div
class={`container-text ${this.hasCenterTitle ? 'has-center-title' : ''}`}
>
<h1 class='title'>{this.title}</h1>
{!this.hasCenterTitle && !isMobile() && <slot />}
</div>
<div class={`atom-meter is-${this.size}`}>
<div
class={`step is-${this.type}`}
style={{ width: `${this.getProgress()}%` }}
/>
</div>
{!this.hasCenterTitle && isMobile() && (
<div class='container-text has-gap-top'>
<slot />
</div>
)}
</Host>
)
}
}
Loading
Loading