Skip to content

Commit

Permalink
Add variant to modal component (#7234)
Browse files Browse the repository at this point in the history
  • Loading branch information
laske185 authored Feb 4, 2025
2 parents 52f455e + 86b1e03 commit 86710f7
Show file tree
Hide file tree
Showing 27 changed files with 181 additions and 19 deletions.
25 changes: 25 additions & 0 deletions packages/components/src/components/modal/modal.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,29 @@ test.describe('kol-modal', () => {
await expect(eventPromise).resolves.toBeUndefined();
});
});

test.describe('kol-modal - variant', () => {
test('it renders the close button in card variant', async ({ page }) => {
await page.setContent('<kol-modal _variant="card" _label="">Modal content</kol-modal>');
const kolModal = page.locator('kol-modal');
const dialog = page.locator('dialog');
await expect(dialog).toBeHidden();
await kolModal.evaluate((element: HTMLKolModalElement) => element.openModal());
await expect(dialog).toBeVisible();
const closeButton = page.locator('.kol-modal__close-button');
await expect(closeButton).toBeVisible();
await closeButton.click();
await expect(dialog).toBeHidden();
});

test('it does not render the close button in blank variant', async ({ page }) => {
await page.setContent('<kol-modal _variant="blank" _label="">Modal content</kol-modal>');
const kolModal = page.locator('kol-modal');
const dialog = page.locator('dialog');
const closeButton = page.locator('.kol-modal__close-button');
await kolModal.evaluate((element: HTMLKolModalElement) => element.openModal());
await expect(dialog).toBeVisible();
await expect(closeButton).toBeHidden();
});
});
});
40 changes: 38 additions & 2 deletions packages/components/src/components/modal/shadow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { setState, validateLabel, watchString } from '../../schema';
import type { JSX } from '@stencil/core';
import { Component, Element, h, Method, Prop, State, Watch } from '@stencil/core';
import { dispatchDomEvent, KolEvent } from '../../utils/events';
import { ModalVariantPropType, validateModalVariant } from '../../schema/props/variant/modal';
import { KolButtonWcTag } from '../../core/component-names';
import { translate } from '../../i18n';
import clsx from 'clsx';

/**
* https://en.wikipedia.org/wiki/Modal_window
Expand Down Expand Up @@ -44,10 +48,16 @@ export class KolModal implements ModalAPI {
this.refDialog?.close?.();
}

private readonly on = {
onClick: async () => {
await this.closeModal();
},
};

public render(): JSX.Element {
return (
<dialog
class="kol-modal"
class={clsx('kol-modal', { 'kol-modal__card': this.state._variant === 'card' })}
ref={(el) => {
this.refDialog = el;
}}
Expand All @@ -58,9 +68,24 @@ export class KolModal implements ModalAPI {
onClose={this.handleNativeCloseEvent.bind(this)}
>
{/* It's necessary to have a block element container for cross-browser compatibility. The display property for the slot content is unknown and could be inline. */}
<div>
<div tabindex="-1">
<slot />
</div>
{this.state._variant === 'card' && (
<KolButtonWcTag
class="kol-modal__close-button"
data-testid="modal-close-button"
_hideLabel
_icons={{
left: {
icon: 'codicon codicon-close',
},
}}
_label={translate('kol-close')}
_on={this.on}
_tooltipAlign="left"
></KolButtonWcTag>
)}
</dialog>
);
}
Expand All @@ -80,6 +105,11 @@ export class KolModal implements ModalAPI {
*/
@Prop() public _width?: string = '100%';

/**
* Defines the variant of the modal.
*/
@Prop() public _variant?: ModalVariantPropType = 'blank';

@State() public state: ModalStates = {
_label: '', // ⚠ required
_width: '100%',
Expand Down Expand Up @@ -110,9 +140,15 @@ export class KolModal implements ModalAPI {
});
}

@Watch('_variant')
public validateVariant(value?: ModalVariantPropType): void {
validateModalVariant(this, value);
}

public componentWillLoad(): void {
this.validateLabel(this._label);
this.validateOn(this._on);
this.validateWidth(this._width);
this.validateVariant(this._variant);
}
}
11 changes: 10 additions & 1 deletion packages/components/src/components/modal/style.scss
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
@import '../@shared/mixins';
@import '../../styles/global';
@import '../tooltip/style.scss';

@layer kol-component {
.kol-modal {
border: 0;
padding: 0;
font-size: rem(16);
position: relative;

&::backdrop {
background-color: rgba(0, 0, 0, 0.33);
}

&__close-button {
position: absolute;
top: rem(1);
right: rem(1);
z-index: 1;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ exports[`kol-modal should render with _label="Label" _width="80%" 1`] = `
<kol-modal>
<template shadowrootmode="open">
<dialog aria-label="Label" class="kol-modal" style="width: 80%;">
<div>
<div tabindex="-1">
<slot></slot>
</div>
</dialog>
Expand Down
5 changes: 3 additions & 2 deletions packages/components/src/schema/components/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import type { Generic } from 'adopted-style-sheets';

import type { PropLabel } from '../props';
import type { KoliBriModalEventCallbacks } from '../types';
import { PropModalVariant } from '../props/variant/modal';

type RequiredProps = PropLabel;
type OptionalProps = {
on: KoliBriModalEventCallbacks;
width: string;
};
} & PropModalVariant;
type RequiredStates = {
width: string;
} & PropLabel;
type OptionalStates = {
on: KoliBriModalEventCallbacks;
};
} & PropModalVariant;

export type ModalProps = Generic.Element.Members<RequiredProps, OptionalProps>;
export type ModalStates = Generic.Element.Members<RequiredStates, OptionalStates>;
Expand Down
24 changes: 24 additions & 0 deletions packages/components/src/schema/props/variant/modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Generic } from 'adopted-style-sheets';

import { watchValidator } from '../../utils/prop.validators';

const ModalVariantPropTypeOptions = ['blank', 'card'] as const;
export type ModalVariantPropType = (typeof ModalVariantPropTypeOptions)[number];

/**
* Defines the different variants for displaying the Modal.
*/
export type PropModalVariant = {
variant: ModalVariantPropType;
};

/* validator */
export const validateModalVariant = (component: Generic.Element.Component, value?: ModalVariantPropType): void => {
watchValidator(
component,
'_variant',
(value) => typeof value === 'string' && ModalVariantPropTypeOptions.includes(value),
new Set(ModalVariantPropTypeOptions),
value,
);
};
40 changes: 28 additions & 12 deletions packages/samples/react/src/components/modal/basic.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FC } from 'react';
import React, { useEffect, useRef } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';

import { KolButton, KolCard, KolModal } from '@public-ui/react';
Expand All @@ -8,14 +8,18 @@ import { SampleDescription } from '../SampleDescription';
export const ModalBasic: FC = () => {
const [searchParams] = useSearchParams();
const modalState = searchParams.get('show-modal') as string;
const defaultVariant = searchParams.get('variant') as string;
const modalElement = useRef<HTMLKolModalElement>(null);
const stackedModalElement = useRef<HTMLKolModalElement>(null);

const [variant, setVariant] = useState<'card' | 'blank'>('blank');
useEffect(() => {
if (modalState === 'true') {
modalElement.current?.openModal();
}
}, [modalState]);
if (defaultVariant === 'card') {
setVariant(defaultVariant);
}
}, [modalState, defaultVariant]);

return (
<>
Expand All @@ -27,7 +31,7 @@ export const ModalBasic: FC = () => {
</SampleDescription>

<div className="flex">
<KolModal _label="Primary modal" _width="80%" ref={modalElement} _on={{ onClose: () => console.log('Modal closed') }}>
<KolModal _label="Primary modal" _width="80%" ref={modalElement} _on={{ onClose: () => console.log('Modal closed') }} _variant={variant}>
<KolCard _label="I am a modal.">
<KolButton
_label="Open stacked modal"
Expand Down Expand Up @@ -62,15 +66,27 @@ export const ModalBasic: FC = () => {
/>
</KolCard>
</KolModal>
<div className="grid gap-4">
<KolButton
_label="Open modal"
_on={{
onClick: () => {
setVariant('blank');
modalElement.current?.openModal();
},
}}
/>

<KolButton
_label="Open modal"
_on={{
onClick: () => {
modalElement.current?.openModal();
},
}}
/>
<KolButton
_label="Open card modal"
_on={{
onClick: () => {
setVariant('card');
modalElement.current?.openModal();
},
}}
/>
</div>
</div>
</>
);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions packages/themes/default/src/components/modal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import '../mixins/rem';

@layer kol-theme-component {
.kol-modal {
&__card {
box-shadow: 0 0 rem(4) var(--color-subtle);
border-radius: var(--border-radius);
}
}
}
2 changes: 2 additions & 0 deletions packages/themes/default/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import inputRangeCss from './components/input-range.scss';
import inputTextCss from './components/input-text.scss';
import kolibriCss from './components/kolibri.scss';
import linkButtonCss from './components/link-button.scss';
import modalCss from './components/modal.scss';
import linkCss from './components/link.scss';
import navCss from './components/nav.scss';
import paginationCss from './components/pagination.scss';
Expand Down Expand Up @@ -70,6 +71,7 @@ export const DEFAULT = KoliBri.createTheme('default', {
'KOL-KOLIBRI': kolibriCss,
'KOL-LINK': linkCss,
'KOL-LINK-BUTTON': linkButtonCss,
'KOL-MODAL': modalCss,
'KOL-NAV': navCss,
'KOL-PAGINATION': paginationCss,
'KOL-PROGRESS': progressCss,
Expand Down
9 changes: 9 additions & 0 deletions packages/themes/ecl/src/ecl-ec/components/modal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import '../../mixins/rem';

@layer kol-theme-component {
.kol-modal {
&__card {
box-shadow: 0 0 calc(var(--spacing-2xs) / 2) var(--color-black);
}
}
}
2 changes: 2 additions & 0 deletions packages/themes/ecl/src/ecl-ec/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import inputTextCss from './components/input-text.scss';
import linkButtonCss from './components/link-button.scss';
import kolibriCss from './components/kolibri.scss';
import linkCss from './components/link.scss';
import modalCss from './components/modal.scss';
import navCss from './components/nav.scss';
import paginationCss from './components/pagination.scss';
import progressCss from './components/progress.scss';
Expand Down Expand Up @@ -76,6 +77,7 @@ export const ECL_EC = KoliBri.createTheme('ecl-ec', {
'KOL-INPUT-DATE': inputDateCss,
'KOL-INPUT-EMAIL': inputEmailCss,
'KOL-INPUT-FILE': inputFileCss,
'KOL-MODAL': modalCss,
'KOL-SELECT': selectCss,
'KOL-TEXTAREA': textareaCss,
'KOL-TABLE-STATEFUL': tableStatefulCss,
Expand Down
9 changes: 9 additions & 0 deletions packages/themes/ecl/src/ecl-eu/components/modal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import '../../mixins/rem';

@layer kol-theme-component {
.kol-modal {
&__card {
box-shadow: 0 0 calc(var(--spacing-2xs) / 2) var(--color-black);
}
}
}
2 changes: 2 additions & 0 deletions packages/themes/ecl/src/ecl-eu/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import inputTextCss from './components/input-text.scss';
import kolibriCss from './components/kolibri.scss';
import linkButtonCss from './components/link-button.scss';
import linkCss from './components/link.scss';
import modalCss from './components/modal.scss';
import navCss from './components/nav.scss';
import paginationCss from './components/pagination.scss';
import progressCss from './components/progress.scss';
Expand Down Expand Up @@ -74,6 +75,7 @@ export const ECL_EU = KoliBri.createTheme('ecl-eu', {
'KOL-AVATAR': avatarCss,
'KOL-TABS': tabsCss,
'KOL-LINK': linkCss,
'KOL-MODAL': modalCss,
'KOL-BUTTON-LINK': buttonLinkCss,
'KOL-BREADCRUMB': breadcrumbCss,
'KOL-DETAILS': detailsCss,
Expand Down
12 changes: 12 additions & 0 deletions packages/themes/itzbund/src/components/modal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@import '../mixins/rem';

@layer kol-theme-component {
.kol-modal {
&__card {
border-color: var(--border-color);
border-style: solid;
border-width: rem(1);
border-radius: calc(2 * var(--border-radius));
}
}
}
7 changes: 6 additions & 1 deletion packages/tools/visual-tests/tests/sample-app.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,12 @@ ROUTES.set('modal/basic?show-modal=true', {
height: 600,
},
});

ROUTES.set('modal/basic?show-modal=true&variant=card', {
viewportSize: {
width: 1920,
height: 600,
},
});
ROUTES.set('nav/aria-current', {
axe: {
skipFailures: false,
Expand Down

0 comments on commit 86710f7

Please sign in to comment.