@carbon/ibm-products-web-components
Version:
Carbon for IBM Products Web Components
253 lines (250 loc) • 15 kB
JavaScript
/**
* Copyright IBM Corp. 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { describe, it, expect, afterEach } from 'vitest';
import { fixture, oneEvent, html } from '@open-wc/testing';
import { SIDE_PANEL_SIZE, SIDE_PANEL_PLACEMENT } from './defs.js';
import { prefix } from '../../globals/settings.js';
import './side-panel.js';
import { getContent, getSlug, getActionToolbarItems, getSubTitle, getActionItems } from './_story-assets/index.js';
import '@carbon/web-components/es-custom/components/text-input/index.js';
import '@carbon/web-components/es-custom/components/slug/index.js';
/**
* Copyright IBM Corp. 2024, 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
const defaultProps = {
animateTitle: true,
includeOverlay: true,
slideIn: false,
selectorInitialFocus: '',
label: '',
open: true,
placement: SIDE_PANEL_PLACEMENT.RIGHT,
preventCloseOnClickOutside: false,
selectorPageContent: '',
size: SIDE_PANEL_SIZE.MEDIUM,
title: 'Side panel title',
condensedActions: false,
};
const blockClass = `${prefix}--side-panel`;
const template = (props = defaultProps, children = getContent(1)) => html `
<c4p-side-panel
?animate-title=${props.animateTitle}
?include-overlay=${props.includeOverlay && !props.slideIn}
selector-initial-focus=${props.selectorInitialFocus}
label-text="${props.label}"
?open=${props.open}
placement=${props.placement}
?prevent-close-on-click-outside=${props.preventCloseOnClickOutside}
selector-page-content=${props.selectorPageContent}
size=${props.size}
?slide-in=${props.slideIn}
.title=${props.title}
>
${children}
</c4p-side-panel>
`;
describe('c4p-side-panel', () => {
it('should render a side panel', async () => {
const sidePanel = (await fixture(template()));
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.open).toBeTruthy();
expect(sidePanel).toBeDefined();
});
it('should render a side panel on the left', async () => {
var _a, _b, _c;
const sidePanel = (await fixture(template(Object.assign(Object.assign({}, defaultProps), { placement: SIDE_PANEL_PLACEMENT.LEFT }))));
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.placement).toBe('left');
const dialogShadowEl = (_b = (_a = sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`.${blockClass}`)) === null || _b === void 0 ? void 0 : _b[0];
if (dialogShadowEl) {
const transform = (_c = window.getComputedStyle(dialogShadowEl)) === null || _c === void 0 ? void 0 : _c.transform;
expect(transform).toBe('matrix(1, 0, 0, 1, -414, 0)');
}
});
it('should render a side panel on the right', async () => {
var _a, _b, _c;
const sidePanel = (await fixture(template()));
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.placement).toBe('right');
const dialogShadowEl = (_b = (_a = sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`.${blockClass}`)) === null || _b === void 0 ? void 0 : _b[0];
if (dialogShadowEl) {
const transform = (_c = window.getComputedStyle(dialogShadowEl)) === null || _c === void 0 ? void 0 : _c.transform;
expect(transform).toBe('matrix(1, 0, 0, 1, 414, 0)');
}
});
it('should render a side panel with overlay and close when clicked outside', async () => {
var _a, _b;
const sidePanel = (await fixture(template()));
// Expect the side panel has include-overlay attribute
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.includeOverlay).toBeTruthy();
// get the overlay element by class .c4p--side-panel__overlay
const overlayElement = (_b = (_a = sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`.${blockClass}__overlay`)) === null || _b === void 0 ? void 0 : _b[0];
// expect the element is present
expect(overlayElement).toBeDefined();
// ensure its tab index is -1
expect(overlayElement === null || overlayElement === void 0 ? void 0 : overlayElement.getAttribute('tabindex')).toBe('-1');
// ensure open attribute present
expect(overlayElement === null || overlayElement === void 0 ? void 0 : overlayElement.hasAttribute('open')).toBeTruthy();
// ensure the preventCloseOnClickOutside property is disabled
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.preventCloseOnClickOutside).toBeFalsy();
// add event listener `cds-custom-side-panel-beingclosed` event
const eventBeforeClose = oneEvent(sidePanel,
// getting event name ie., `cds-custom-side-panel-beingclosed`
sidePanel.constructor.eventBeforeClose);
// add event listener `cds-custom-side-panel-closed` event
const eventClose = oneEvent(sidePanel,
// getting event name ie., `cds-custom-side-panel-closed`
sidePanel.constructor.eventClose);
// dispatch `cds-custom-side-panel-beingclosed` and `cds-custom-side-panel-closed` events on overlay element click
overlayElement === null || overlayElement === void 0 ? void 0 : overlayElement.dispatchEvent(new Event('click'));
// listen to `cds-custom-side-panel-beingclosed` and `cds-custom-side-panel-closed` event
// and take the details of events
const { detail: beforeCloseDetail } = await eventBeforeClose;
const { detail: closeDetail } = await eventClose;
// make sure both events are triggered by overlay element
expect(beforeCloseDetail === null || beforeCloseDetail === void 0 ? void 0 : beforeCloseDetail.triggeredBy).toBe(overlayElement);
expect(closeDetail === null || closeDetail === void 0 ? void 0 : closeDetail.triggeredBy).toBe(overlayElement);
// expect the side panel is closed
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.open).toBeFalsy();
// expect overlay also closed
expect(overlayElement === null || overlayElement === void 0 ? void 0 : overlayElement.hasAttribute('open')).toBeFalsy();
});
it('should close side panel on escape keydown', async () => {
const sidePanel = (await fixture(template()));
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.open).toBeTruthy();
// add event listener `cds-custom-side-panel-beingclosed` event
const eventBeforeClose = oneEvent(sidePanel,
// getting event name ie., `cds-custom-side-panel-beingclosed`
sidePanel.constructor.eventBeforeClose);
// add event listener `cds-custom-side-panel-closed` event
const eventClose = oneEvent(sidePanel,
// getting event name ie., `cds-custom-side-panel-closed`
sidePanel.constructor.eventClose);
// press the escape key
document === null || document === void 0 ? void 0 : document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
// listen to `cds-custom-side-panel-beingclosed` and `cds-custom-side-panel-closed` event
// and take the details of events
const { detail: beforeCloseDetail } = await eventBeforeClose;
const { detail: closeDetail } = await eventClose;
// make sure both events are triggered from document
expect(beforeCloseDetail === null || beforeCloseDetail === void 0 ? void 0 : beforeCloseDetail.triggeredBy).toBe(document);
expect(closeDetail === null || closeDetail === void 0 ? void 0 : closeDetail.triggeredBy).toBe(document);
// expect the side panel is closed
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.open).toBeFalsy();
});
it('should close side panel on the close button click', async () => {
var _a;
const sidePanel = (await fixture(template()));
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.open).toBeTruthy();
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.closeIconDescription).toBe('Close');
// get the close button
const closeButton = (_a = sidePanel.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('cds-custom-icon-button');
// ensure the close button is present
expect(closeButton).toBeDefined();
// add event listener `cds-custom-side-panel-beingclosed` event
const eventBeforeClose = oneEvent(sidePanel,
// getting event name ie., `cds-custom-side-panel-beingclosed`
sidePanel.constructor.eventBeforeClose);
// add event listener `cds-custom-side-panel-closed` event
const eventClose = oneEvent(sidePanel,
// getting event name ie., `cds-custom-side-panel-closed`
sidePanel.constructor.eventClose);
// click on the close button
closeButton === null || closeButton === void 0 ? void 0 : closeButton.dispatchEvent(new Event('click'));
// listen to `cds-custom-side-panel-beingclosed` and `cds-custom-side-panel-closed` event
// and take the details of events
const { detail: beforeCloseDetail } = await eventBeforeClose;
const { detail: closeDetail } = await eventClose;
// make sure both events are triggered by overlay element
expect(beforeCloseDetail === null || beforeCloseDetail === void 0 ? void 0 : beforeCloseDetail.triggeredBy).toBe(closeButton);
expect(closeDetail === null || closeDetail === void 0 ? void 0 : closeDetail.triggeredBy).toBe(closeButton);
// expect the side panel is closed
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.open).toBeFalsy();
});
it('should render a slug', async () => {
const sidePanel = (await fixture(template(defaultProps, getSlug(1))));
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.open).toBeTruthy();
// ensure there is a slug present
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel._hasSlug).toBeTruthy();
// locate slug element
const slug = sidePanel.querySelector('cds-custom-slug');
// expect the slug element is present
expect(slug).toBeDefined();
// expect the default slug size is xs
expect(slug.size).toBe('xs');
});
it('should render navigation button', async () => {
const sidePanel = (await fixture(template(defaultProps, getActionToolbarItems(1))));
expect(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.open).toBeTruthy();
// get the copy button
const copyButton = Array.prototype.find.call(sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.querySelectorAll('cds-custom-button'), (el) => el.innerHTML === 'Copy');
// expect copy button to be present
expect(copyButton).toBeDefined();
});
it('should render a slide-in side panel', async () => {
var _a, _b;
const sidePanel = (await fixture(template(Object.assign(Object.assign({}, defaultProps), { slideIn: true }))));
expect(sidePanel.slideIn).toBeTruthy();
expect(sidePanel.includeOverlay).toBeFalsy();
// try to get the overlay element by class .c4p--side-panel__overlay
const overlayElement = (_b = (_a = sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`.${blockClass}__overlay`)) === null || _b === void 0 ? void 0 : _b[0];
// expect the element is not present
expect(overlayElement).not.toBeDefined();
});
it('should call the eventNavigateBack callback', async () => {
var _a;
const prevStep = () => {
var _a;
(_a = document
.querySelector(`${prefix}-side-panel`)) === null || _a === void 0 ? void 0 : _a.setAttribute('current-step', '1');
};
const sidePanel = (await fixture(html `<c4p-side-panel
?animate-title=${defaultProps.animateTitle}
?condensed-actions=${defaultProps.condensedActions}
current-step="1"
?include-overlay=${defaultProps.includeOverlay && !defaultProps.slideIn}
selector-initial-focus=${defaultProps.selectorInitialFocus}
label-text="${defaultProps.label}"
?open=${defaultProps.open}
placement=${defaultProps.placement}
?prevent-close-on-click-outside=${defaultProps.preventCloseOnClickOutside}
selector-page-content=${defaultProps.selectorPageContent}
size=${defaultProps.size}
?slide-in=${defaultProps.slideIn}
.title=${defaultProps.title}
@c4p-side-panel-navigate-back=${prevStep}
>${getContent(1)}</<c4p-side-panel`));
const backButton = (_a = sidePanel.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`.${prefix}--side-panel__navigation-back-button`);
// create event listener
const eventNavigateBack = oneEvent(sidePanel, sidePanel.constructor.eventNavigateBack);
// dispatch the event from click of back button
backButton === null || backButton === void 0 ? void 0 : backButton.dispatchEvent(new Event('click'));
// expect the detail object of eventNavigateBack method
const { detail } = await eventNavigateBack;
expect(detail).toBeDefined();
});
it('should render subtitle text', async () => {
const sidePanel = (await fixture(template(defaultProps, getSubTitle(1))));
const subTitleText = sidePanel === null || sidePanel === void 0 ? void 0 : sidePanel.querySelector(`.${prefix}--side-panel__subtitle-text`);
expect(subTitleText).toBeDefined();
});
it('should render at least one action item', async () => {
const sidePanel = (await fixture(template(defaultProps, getActionItems(1))));
const actionItems = sidePanel.querySelectorAll(`.${prefix}--action-set__action-button`);
expect(actionItems).toHaveLength(1);
});
it('should render only 3 action items even if too many items exists', async () => {
const sidePanel = (await fixture(template(defaultProps, getActionItems(6))));
const actionItems = sidePanel.querySelectorAll(`.${prefix}--action-set__action-button`);
expect(actionItems).toHaveLength(3);
});
afterEach(() => {
// Clears the DOM after each test
document.body.innerHTML = '';
});
});
//# sourceMappingURL=side-panel.test.js.map