diff --git a/README.md b/README.md index f532e07b9..7ce089131 100644 --- a/README.md +++ b/README.md @@ -130,11 +130,11 @@ To locally test Atomium using Alpha/Beta versions, follow the steps below: 1. Update the `.release-please-manifest.json` file in the root directory of the Atomium project with the next version number + alpha. Ex: the current version is `2.10.0`, so the next alpha version can be `2.11.0-alpha.1` (OBS: in the example, it is updating only the core lib, update the libs that your changes impact). -![add alpha version to release](https://github.com/user-attachments/assets/3e3adaa2-1bcd-4442-8d1a-118cc14cc274) +![add alpha version to release](https://github.com/user-attachments/assets/91418116-266c-45f1-9cf2-bdf2d6c1a7eb) 2. Add the same version to the repespective `package.json` file in the root directory of the lib project. Ex: packages/core/package.json -![add alpha to package](https://github.com/user-attachments/assets/3e3adaa2-1bcd-4442-8d1a-118cc14cc274) +![add alpha to package](https://github.com/user-attachments/assets/ec4bfbf2-a822-4cf5-b7c4-66575fd36230) 3. Build the Atomium libraries by running the following command in the root directory of the Atomium project diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index fc758be82..b27df1b17 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -165,6 +165,7 @@ export namespace Components { "hasDivider": boolean; "hasFooter": boolean; "headerTitle": string; + "isOpen": boolean; "primaryText"?: string; "progress"?: number; "secondaryText"?: string; @@ -703,6 +704,7 @@ declare namespace LocalJSX { "hasDivider"?: boolean; "hasFooter"?: boolean; "headerTitle"?: string; + "isOpen"?: boolean; "onAtomCloseClick"?: (event: AtomModalCustomEvent) => void; "onAtomDidDismiss"?: (event: AtomModalCustomEvent) => void; "onAtomDidPresent"?: (event: AtomModalCustomEvent) => void; diff --git a/packages/core/src/components/modal/modal.scss b/packages/core/src/components/modal/modal.scss index 34a1dbe51..2fa0c9766 100644 --- a/packages/core/src/components/modal/modal.scss +++ b/packages/core/src/components/modal/modal.scss @@ -1,6 +1,8 @@ @import '~@atomium/scss-utils/index'; .atom-modal { + position: fixed; + &__close { position: absolute; right: var(--spacing-small); diff --git a/packages/core/src/components/modal/modal.spec.ts b/packages/core/src/components/modal/modal.spec.ts index d00e18c2a..a66ecbcc1 100644 --- a/packages/core/src/components/modal/modal.spec.ts +++ b/packages/core/src/components/modal/modal.spec.ts @@ -2,6 +2,20 @@ import { newSpecPage, SpecPage } from '@stencil/core/testing' import { AtomModal } from './modal' +Object.defineProperty(document.body, 'classList', { + value: { + add: jest.fn(), + remove: jest.fn(), + }, +}) + +Object.defineProperty(document.documentElement, 'classList', { + value: { + add: jest.fn(), + remove: jest.fn(), + }, +}) + describe('atom-modal', () => { let page: SpecPage @@ -69,6 +83,114 @@ describe('atom-modal', () => { expect(page.root?.textContent).toContain('Header from prop') }) + it('should render modal closed if is open is set to false', async () => { + page = await newSpecPage({ + components: [AtomModal], + html: ` + + Modal content +
Custom Header
+
+ `, + }) + + expect(page.root?.innerHTML).not.toContain('isopen') + }) + it('should render modal opened if is open is set to true', async () => { + page = await newSpecPage({ + components: [AtomModal], + html: ` + + Modal content +
Custom Header
+
+ `, + }) + + expect(page.root?.innerHTML).toContain('isopen') + + page.rootInstance.isOpen = false + + await page.waitForChanges() + + expect(document.body.classList.remove).toHaveBeenCalled() + }) + it('should call remove and add on backdrop no scroll class when is-open changes', async () => { + page = await newSpecPage({ + components: [AtomModal], + html: ` + + Modal content +
Custom Header
+
+ `, + }) + + expect(page.root?.innerHTML).toContain('isopen') + + page.rootInstance.modal = { + dismiss: jest.fn(), + close: jest.fn(), + } + page.rootInstance.handleDidPresent() + + expect(document.body.classList.add).toHaveBeenCalled() + expect(document.documentElement.classList.add).toHaveBeenCalled() + + page.rootInstance.handleDidDismiss() + + expect(page.rootInstance.modal.close).toHaveBeenCalled() + + page.rootInstance.handleCloseClick() + + expect(page.rootInstance.modal.close).toHaveBeenCalled() + }) + + it('should call remove and add on backdrop no scroll class when is-open changes', async () => { + page = await newSpecPage({ + components: [AtomModal], + html: ` + + Modal content +
Custom Header
+
+ `, + }) + + expect(page.root?.innerHTML).toContain('isopen') + + page.rootInstance.modal = { + dismiss: jest.fn(), + } + + page.rootInstance.handleDidPresent() + + expect(document.body.classList.add).toHaveBeenCalled() + expect(document.documentElement.classList.add).toHaveBeenCalled() + }) + + it('should call remove on class list when call remove classes', async () => { + page = await newSpecPage({ + components: [AtomModal], + html: ` + + Modal content +
Custom Header
+
+ `, + }) + + page.rootInstance.addClasses() + + expect(document.body.classList.add).toHaveBeenCalled() + expect(document.documentElement.classList.add).toHaveBeenCalled() + + page.rootInstance.removeClasses() + + expect(document.body.classList.remove).toHaveBeenCalled() + expect(document.documentElement.classList.remove).toHaveBeenCalled() + }) + it('should render icon type when alertType is passed', async () => { await page.setContent(` diff --git a/packages/core/src/components/modal/modal.tsx b/packages/core/src/components/modal/modal.tsx index 42a0805fd..4b32552e7 100644 --- a/packages/core/src/components/modal/modal.tsx +++ b/packages/core/src/components/modal/modal.tsx @@ -4,12 +4,11 @@ import { IconProps } from '../../icons' type AlertType = Record<'alert' | 'error', { icon: IconProps; color: string }> -/* @todo it's needed to prevent a ionic error. In the version 8.0 it was fixed, remove it after the upgrade. - * https://github.com/ionic-team/ionic-framework/issues/23942 - */ const BACKDROP_NO_SCROLL = 'backdrop-no-scroll' -type HTMLAtomModalElement = HTMLIonModalElement & { close: () => Promise } +export type HTMLAtomModalElement = HTMLIonModalElement & { + close: () => Promise +} @Component({ tag: 'atom-modal', @@ -28,6 +27,7 @@ export class AtomModal { @Prop() hasFooter = true @Prop() disablePrimary = false @Prop() disableSecondary = false + @Prop({ mutable: true }) isOpen = false @Event() atomCloseClick: EventEmitter @Event() atomDidDismiss: EventEmitter @@ -37,7 +37,7 @@ export class AtomModal { private modal: HTMLAtomModalElement - private alertMap: AlertType = { + private readonly alertMap: AlertType = { alert: { icon: 'alert-outline', color: 'warning', @@ -48,34 +48,50 @@ export class AtomModal { }, } + private readonly addClasses = () => { + document.body.classList.add(BACKDROP_NO_SCROLL) + document.documentElement.classList.add(BACKDROP_NO_SCROLL) + } + + private readonly removeClasses = () => { + document.body.classList.remove(BACKDROP_NO_SCROLL) + document.documentElement.classList.remove(BACKDROP_NO_SCROLL) + } + componentDidLoad() { + /* @todo it's needed to prevent a ionic error. In the version 8.0 it was fixed, remove it after the upgrade. + * https://github.com/ionic-team/ionic-framework/issues/23942 + */ document.body.classList.remove(BACKDROP_NO_SCROLL) this.modal.close = async () => { await this.modal.dismiss() - - document.body.classList.remove(BACKDROP_NO_SCROLL) + this.removeClasses() + this.isOpen = false } } - private handleDidDimiss = () => { + private readonly handleDidDismiss = () => { this.atomDidDismiss.emit(this.modal) + this.modal.close() } - private handleDidPresent = () => { + private readonly handleDidPresent = () => { this.atomDidPresent.emit(this.modal) + this.isOpen = true + this.addClasses() } - private handleCloseClick = async () => { + private readonly handleCloseClick = async () => { this.atomCloseClick.emit(this.modal) this.modal.close() } - private handleSecondaryClick = () => { + private readonly handleSecondaryClick = () => { this.atomSecondaryClick.emit(this.modal) } - private handlePrimaryClick = () => { + private readonly handlePrimaryClick = () => { this.atomPrimaryClick.emit(this.modal) } @@ -93,8 +109,10 @@ export class AtomModal { 'atom-modal': true, 'atom-modal--progress': !!this.progress, }} - onIonModalDidDismiss={this.handleDidDimiss} + onIonModalDidDismiss={this.handleDidDismiss} + onDidDismiss={this.handleDidDismiss} onDidPresent={this.handleDidPresent} + isOpen={this.isOpen} >
{iconType && ( diff --git a/packages/core/src/components/modal/stories/modal.args.ts b/packages/core/src/components/modal/stories/modal.args.ts index 5c58e274b..b2801f383 100644 --- a/packages/core/src/components/modal/stories/modal.args.ts +++ b/packages/core/src/components/modal/stories/modal.args.ts @@ -80,6 +80,14 @@ export const ModalStoryArgs = { category: Category.PROPERTIES, }, }, + isOpen: { + control: 'boolean', + description: 'If true, the modal will be opened. Default is false', + table: { + category: Category.PROPERTIES, + }, + defaultValue: false, + }, disableSecondary: { control: 'boolean', description: @@ -161,4 +169,5 @@ export const ModalComponentArgs = { hasDivider: false, disableSecondary: false, disablePrimary: false, + isOpen: false, } diff --git a/packages/core/src/components/modal/stories/modal.core.stories.tsx b/packages/core/src/components/modal/stories/modal.core.stories.tsx index 8ef8864aa..039bcface 100644 --- a/packages/core/src/components/modal/stories/modal.core.stories.tsx +++ b/packages/core/src/components/modal/stories/modal.core.stories.tsx @@ -23,6 +23,7 @@ const createModal = (args) => { header-title="${args.headerTitle}" disable-secondary="${args.disableSecondary}" disable-primary="${args.disablePrimary}" + is-open="${args.isOpen}" >
Custom Header

Modal Content

diff --git a/packages/core/src/components/modal/stories/modal.react.stories.tsx b/packages/core/src/components/modal/stories/modal.react.stories.tsx index 98b39f102..2d9172033 100644 --- a/packages/core/src/components/modal/stories/modal.react.stories.tsx +++ b/packages/core/src/components/modal/stories/modal.react.stories.tsx @@ -22,6 +22,7 @@ const createModal = (args) => ( progress={args.progress} disablePrimary={args.disablePrimary} disableSecondary={args.disableSecondary} + isOpen={args.isOpen} >
Custom Header

Modal Content

diff --git a/packages/core/src/components/modal/stories/modal.vue.stories.tsx b/packages/core/src/components/modal/stories/modal.vue.stories.tsx index 20da66fb4..196ae3e71 100644 --- a/packages/core/src/components/modal/stories/modal.vue.stories.tsx +++ b/packages/core/src/components/modal/stories/modal.vue.stories.tsx @@ -25,6 +25,7 @@ const createModal = (args, themeColor = 'light') => ({ progress="${args.progress}" disable-primary="${args.disablePrimary}" disable-secondary="${args.disableSecondary}" + is-open="${args.isOpen}" > {{ args.label }} diff --git a/packages/core/src/global/global.scss b/packages/core/src/global/global.scss index 3ac44b6ac..29f9b2809 100644 --- a/packages/core/src/global/global.scss +++ b/packages/core/src/global/global.scss @@ -111,3 +111,7 @@ Issue: https://github.com/ionic-team/ionic-framework/issues/27500 } } } + +html.backdrop-no-scroll { + overflow: hidden; +} \ No newline at end of file