Skip to content

Commit

Permalink
feat(component): add steps modal (#599)
Browse files Browse the repository at this point in the history
  • Loading branch information
jowjow22 authored Nov 4, 2024
1 parent 67fbf8c commit 38a28ce
Show file tree
Hide file tree
Showing 15 changed files with 876 additions and 26 deletions.
8 changes: 6 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/core/sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ src/**/*.spec.tsx,\
output-target/*.spec.ts
sonar.coverage.exclusions=\
src/components/grid/col/col.tsx,\
output-target/*.spec.ts
output-target/*.spec.ts,\
output-target/index.ts
sonar.cpd.exclusions=\
src/components/textarea/**
sonar.exclusions=\
Expand Down
61 changes: 61 additions & 0 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,16 @@ export namespace Components {
"setTagInSelectOptions": () => Promise<void>;
"value"?: IonTypes.IonSelect['value'];
}
interface AtomStepsModal {
"closeOnFinish"?: boolean;
"currentStep": number;
"isOpen": boolean;
"primaryText"?: string;
"secondaryText"?: string;
"steps": number;
"stepsTitles": string;
"trigger"?: string;
}
interface AtomTag {
"color": 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light';
"customBackgroundColor"?: string;
Expand Down Expand Up @@ -286,6 +296,10 @@ export interface AtomSelectCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLAtomSelectElement;
}
export interface AtomStepsModalCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLAtomStepsModalElement;
}
export interface AtomTextareaCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLAtomTextareaElement;
Expand Down Expand Up @@ -458,6 +472,7 @@ declare global {
"atomDidPresent": any;
"atomPrimaryClick": any;
"atomSecondaryClick": any;
"atomIsOpenChange": any;
}
interface HTMLAtomModalElement extends Components.AtomModal, HTMLStencilElement {
addEventListener<K extends keyof HTMLAtomModalElementEventMap>(type: K, listener: (this: HTMLAtomModalElement, ev: AtomModalCustomEvent<HTMLAtomModalElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
Expand Down Expand Up @@ -494,6 +509,30 @@ declare global {
prototype: HTMLAtomSelectElement;
new (): HTMLAtomSelectElement;
};
interface HTMLAtomStepsModalElementEventMap {
"atomFinish": any;
"atomCancel": any;
"atomNextStep": any;
"atomPreviousStep": any;
"atomCloseClick": any;
"atomDidDismiss": any;
"atomDidPresent": any;
"atomIsOpenChange": any;
}
interface HTMLAtomStepsModalElement extends Components.AtomStepsModal, HTMLStencilElement {
addEventListener<K extends keyof HTMLAtomStepsModalElementEventMap>(type: K, listener: (this: HTMLAtomStepsModalElement, ev: AtomStepsModalCustomEvent<HTMLAtomStepsModalElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof HTMLAtomStepsModalElementEventMap>(type: K, listener: (this: HTMLAtomStepsModalElement, ev: AtomStepsModalCustomEvent<HTMLAtomStepsModalElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
var HTMLAtomStepsModalElement: {
prototype: HTMLAtomStepsModalElement;
new (): HTMLAtomStepsModalElement;
};
interface HTMLAtomTagElement extends Components.AtomTag, HTMLStencilElement {
}
var HTMLAtomTagElement: {
Expand Down Expand Up @@ -538,6 +577,7 @@ declare global {
"atom-list-slider-item": HTMLAtomListSliderItemElement;
"atom-modal": HTMLAtomModalElement;
"atom-select": HTMLAtomSelectElement;
"atom-steps-modal": HTMLAtomStepsModalElement;
"atom-tag": HTMLAtomTagElement;
"atom-textarea": HTMLAtomTextareaElement;
}
Expand Down Expand Up @@ -708,6 +748,7 @@ declare namespace LocalJSX {
"onAtomCloseClick"?: (event: AtomModalCustomEvent<any>) => void;
"onAtomDidDismiss"?: (event: AtomModalCustomEvent<any>) => void;
"onAtomDidPresent"?: (event: AtomModalCustomEvent<any>) => void;
"onAtomIsOpenChange"?: (event: AtomModalCustomEvent<any>) => void;
"onAtomPrimaryClick"?: (event: AtomModalCustomEvent<any>) => void;
"onAtomSecondaryClick"?: (event: AtomModalCustomEvent<any>) => void;
"primaryText"?: string;
Expand Down Expand Up @@ -742,6 +783,24 @@ declare namespace LocalJSX {
"readonly"?: boolean;
"value"?: IonTypes.IonSelect['value'];
}
interface AtomStepsModal {
"closeOnFinish"?: boolean;
"currentStep"?: number;
"isOpen"?: boolean;
"onAtomCancel"?: (event: AtomStepsModalCustomEvent<any>) => void;
"onAtomCloseClick"?: (event: AtomStepsModalCustomEvent<any>) => void;
"onAtomDidDismiss"?: (event: AtomStepsModalCustomEvent<any>) => void;
"onAtomDidPresent"?: (event: AtomStepsModalCustomEvent<any>) => void;
"onAtomFinish"?: (event: AtomStepsModalCustomEvent<any>) => void;
"onAtomIsOpenChange"?: (event: AtomStepsModalCustomEvent<any>) => void;
"onAtomNextStep"?: (event: AtomStepsModalCustomEvent<any>) => void;
"onAtomPreviousStep"?: (event: AtomStepsModalCustomEvent<any>) => void;
"primaryText"?: string;
"secondaryText"?: string;
"steps"?: number;
"stepsTitles"?: string;
"trigger"?: string;
}
interface AtomTag {
"color"?: 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light';
"customBackgroundColor"?: string;
Expand Down Expand Up @@ -820,6 +879,7 @@ declare namespace LocalJSX {
"atom-list-slider-item": AtomListSliderItem;
"atom-modal": AtomModal;
"atom-select": AtomSelect;
"atom-steps-modal": AtomStepsModal;
"atom-tag": AtomTag;
"atom-textarea": AtomTextarea;
}
Expand All @@ -845,6 +905,7 @@ declare module "@stencil/core" {
"atom-list-slider-item": LocalJSX.AtomListSliderItem & JSXBase.HTMLAttributes<HTMLAtomListSliderItemElement>;
"atom-modal": LocalJSX.AtomModal & JSXBase.HTMLAttributes<HTMLAtomModalElement>;
"atom-select": LocalJSX.AtomSelect & JSXBase.HTMLAttributes<HTMLAtomSelectElement>;
"atom-steps-modal": LocalJSX.AtomStepsModal & JSXBase.HTMLAttributes<HTMLAtomStepsModalElement>;
"atom-tag": LocalJSX.AtomTag & JSXBase.HTMLAttributes<HTMLAtomTagElement>;
"atom-textarea": LocalJSX.AtomTextarea & JSXBase.HTMLAttributes<HTMLAtomTextareaElement>;
}
Expand Down
50 changes: 38 additions & 12 deletions packages/core/src/components/modal/modal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,18 +203,6 @@ describe('atom-modal', () => {
)
})

it('should render progress bar when progress is passed', async () => {
await page.setContent(`
<atom-modal progress="0.5">
Modal content
</atom-modal>
`)

expect(page.root?.innerHTML).toContain(
'<ion-progress-bar value="0.5" color="primary">'
)
})

it('should content contain divided class', async () => {
await page.setContent(`
<atom-modal has-divider="true">
Expand Down Expand Up @@ -253,4 +241,42 @@ describe('atom-modal', () => {
expect(spyPrimary).toHaveBeenCalled()
expect(spySecondary).toHaveBeenCalled()
})
it('should render progress bar when progress is passed even if it is zero', async () => {
await page.setContent(`
<atom-modal>
Modal content
</atom-modal>
`)

await page.waitForChanges()

expect(page.root?.innerHTML).not.toContain(
'<ion-progress-bar value="0" color="primary"></ion-progress-bar>'
)

page = await newSpecPage({
components: [AtomModal],
html: `
<atom-modal progress="0">
Modal content
</atom-modal>
`,
})

expect(page.root?.innerHTML).toContain(
'<ion-progress-bar value="0" color="primary"></ion-progress-bar>'
)
})
it('should emit atomIsOpenChange when is open changes', async () => {
const isOpenChangeSpy = jest.fn()

page.root?.addEventListener('atomIsOpenChange', isOpenChangeSpy)

page.rootInstance.isOpen = true

await page.waitForChanges()

expect(isOpenChangeSpy).toHaveBeenCalled()
expect(isOpenChangeSpy.mock.calls[0][0].detail).toBe(true)
})
})
23 changes: 19 additions & 4 deletions packages/core/src/components/modal/modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Component, Event, EventEmitter, Host, Prop, h } from '@stencil/core'
import {
Component,
Event,
EventEmitter,
Host,
Prop,
Watch,
h,
} from '@stencil/core'

import { IconProps } from '../../icons'

Expand Down Expand Up @@ -34,6 +42,7 @@ export class AtomModal {
@Event() atomDidPresent: EventEmitter
@Event() atomPrimaryClick: EventEmitter
@Event() atomSecondaryClick: EventEmitter
@Event() atomIsOpenChange: EventEmitter

private modal: HTMLAtomModalElement

Expand Down Expand Up @@ -71,14 +80,20 @@ export class AtomModal {
}
}

@Watch('isOpen')
handleIsOpenChange(newValue: boolean, oldValue: boolean) {
if (newValue !== oldValue) {
this.atomIsOpenChange.emit(newValue)
}
}

private readonly handleDidDismiss = () => {
this.atomDidDismiss.emit(this.modal)
this.modal.close()
}

private readonly handleDidPresent = () => {
this.atomDidPresent.emit(this.modal)
this.isOpen = true
this.addClasses()
}

Expand Down Expand Up @@ -143,9 +158,9 @@ export class AtomModal {
></atom-icon>
</atom-button>
</header>
{!!this.progress && (
{this.progress >= 0 ? (
<ion-progress-bar value={this.progress} color='primary' />
)}
) : null}
<div
id='atom-modal__content'
part='content'
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/components/modal/stories/modal.args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,39 +107,39 @@ export const ModalStoryArgs = {
atomCloseClick: {
action: 'atomCloseClick',
description:
'Event emitted when the close button is clicked. The event callback recieve ion-modal custom component as a param.',
'Event emitted when the close button is clicked. The event callback recieve `ion-modal` custom component as a param.',
table: {
category: Category.EVENTS,
},
},
atomDidDismiss: {
action: 'atomDidDismiss',
description:
'Event emitted after the modal has dismissed. The event callback recieve ion-modal custom component as a param.',
'Event emitted after the modal has dismissed. The event callback recieve `ion-modal` custom component as a param.',
table: {
category: Category.EVENTS,
},
},
atomDidPresent: {
action: 'atomDidPresent',
description:
'Event emitted after the modal has presented. The event callback recieve ion-modal custom component as a param.',
'Event emitted after the modal has presented. The event callback recieve `ion-modal` custom component as a param.',
table: {
category: Category.EVENTS,
},
},
atomPrimaryClick: {
action: 'atomPrimaryClick',
description:
'Event emitted when the primary button is clicked. The event callback recieve ion-modal custom component as a param.',
'Event emitted when the primary button is clicked. The event callback recieve `ion-modal` custom component as a param.',
table: {
category: Category.EVENTS,
},
},
atomSecondaryClick: {
action: 'atomSecondaryClick',
description:
'Event emitted when the secondary button is clicked. The event callback recieve ion-modal custom component as a param.',
'Event emitted when the secondary button is clicked. The event callback recieve `ion-modal` custom component as a param.',
table: {
category: Category.EVENTS,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Meta, StoryObj } from '@storybook/vue3'
import { ModalComponentArgs, ModalStoryArgs } from './modal.args'

export default {
title: 'Components/Button',
title: 'Components/Modal',
...ModalStoryArgs,
} as Meta

Expand All @@ -27,7 +27,8 @@ const createModal = (args, themeColor = 'light') => ({
disable-secondary="${args.disableSecondary}"
is-open="${args.isOpen}"
>
{{ args.label }}
<div slot='header'>Custom Header</div>
<p>Modal Content</p>
</AtomModal>
</div>
`,
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/components/steps-modal/steps-modal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import '~@atomium/scss-utils/index';

.atom-steps-modal__step {
margin-top: var(--spacing-base);
}
Loading

0 comments on commit 38a28ce

Please sign in to comment.