| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import { |
| assertElements, |
| renderElementIntoDOM, |
| } from '../../../testing/DOMHelpers.js'; |
| import {Icon} from '../../kit/kit.js'; |
| |
| import * as IconButton from './icon_button.js'; |
| |
| const renderIconButton = (data: IconButton.IconButton.IconButtonData): |
| {component: IconButton.IconButton.IconButton, shadowRoot: ShadowRoot} => { |
| const component = new IconButton.IconButton.IconButton(); |
| component.data = data; |
| renderElementIntoDOM(component); |
| assert.isNotNull(component.shadowRoot); |
| return {component, shadowRoot: component.shadowRoot}; |
| }; |
| |
| const defaultIcon: IconButton.IconButton.IconWithTextData = { |
| iconName: 'cross-circle', |
| iconColor: 'var(--icon-error)', |
| text: '1', |
| }; |
| |
| export const extractIconGroups = (shadowRoot: ShadowRoot) => { |
| const icons = shadowRoot.querySelectorAll('.status-icon'); |
| assertElements(icons, Icon); |
| const labels = shadowRoot.querySelectorAll('.icon-button-title'); |
| assertElements(labels, HTMLSpanElement); |
| assert.strictEqual(icons.length, labels.length, 'Expected icons and labels to appear in pairs'); |
| const iconGroups = []; |
| for (let i = 0; i < icons.length; ++i) { |
| const labelElement = labels[i]; |
| const label = window.getComputedStyle(labelElement).display === 'none' ? null : labelElement.textContent; |
| iconGroups.push({iconData: icons[i].data, label}); |
| } |
| return iconGroups; |
| }; |
| |
| describe('IconButton', () => { |
| it('renders correctly with one icon', () => { |
| const {shadowRoot} = renderIconButton({clickHandler: () => {}, groups: [defaultIcon]}); |
| |
| const icons = extractIconGroups(shadowRoot); |
| assert.lengthOf(icons, 1); |
| assert.deepEqual(icons.map(c => c.label), ['1']); |
| const iconNames = icons.map(c => 'iconName' in c.iconData ? c.iconData.iconName : undefined); |
| assert.deepEqual(iconNames, ['cross-circle']); |
| }); |
| |
| it('renders correctly with two icons', () => { |
| const {shadowRoot} = renderIconButton({ |
| clickHandler: () => {}, |
| groups: [ |
| defaultIcon, |
| { |
| iconName: 'warning', |
| iconColor: 'var(--icon-warning)', |
| text: '12', |
| }, |
| ], |
| }); |
| |
| const icons = extractIconGroups(shadowRoot); |
| assert.lengthOf(icons, 2); |
| assert.deepEqual(icons.map(c => c.label), ['1', '12']); |
| const iconNames = icons.map(c => 'iconName' in c.iconData ? c.iconData.iconName : undefined); |
| assert.deepEqual(iconNames, ['cross-circle', 'warning']); |
| }); |
| |
| describe('compact mode', () => { |
| it('renders correctly with one icon', () => { |
| const {shadowRoot} = renderIconButton({clickHandler: () => {}, groups: [defaultIcon], compact: true}); |
| |
| const icons = extractIconGroups(shadowRoot); |
| assert.lengthOf(icons, 1); |
| assert.deepEqual(icons.map(c => c.label), [null]); |
| const iconNames = icons.map(c => 'iconName' in c.iconData ? c.iconData.iconName : undefined); |
| assert.deepEqual(iconNames, ['cross-circle']); |
| }); |
| |
| it('renders correctly with two icons', () => { |
| const {shadowRoot} = renderIconButton({ |
| clickHandler: () => {}, |
| groups: [ |
| defaultIcon, |
| { |
| iconName: 'warning', |
| iconColor: 'var(--icon-warning)', |
| text: '12', |
| }, |
| ], |
| compact: true, |
| }); |
| |
| const icons = extractIconGroups(shadowRoot); |
| assert.lengthOf(icons, 1); |
| assert.deepEqual(icons.map(c => c.label), [null]); |
| const iconNames = icons.map(c => 'iconName' in c.iconData ? c.iconData.iconName : undefined); |
| assert.deepEqual(iconNames, ['cross-circle']); |
| }); |
| }); |
| |
| it('renders correctly with two icons where one text is undefined', () => { |
| const {shadowRoot} = renderIconButton({ |
| clickHandler: () => {}, |
| groups: [ |
| { |
| iconName: 'warning', |
| iconColor: 'var(--icon-warning)', |
| text: undefined, |
| }, |
| defaultIcon, |
| ], |
| }); |
| |
| const icons = extractIconGroups(shadowRoot); |
| assert.lengthOf(icons, 1); |
| assert.deepEqual(icons.map(c => c.label), ['1']); |
| const iconNames = icons.map(c => 'iconName' in c.iconData ? c.iconData.iconName : undefined); |
| assert.deepEqual(iconNames, ['cross-circle']); |
| }); |
| |
| it('renders correctly with a customly sized icon', () => { |
| const {shadowRoot} = renderIconButton({ |
| clickHandler: () => {}, |
| groups: [ |
| { |
| iconName: 'warning', |
| iconColor: 'var(--icon-warning)', |
| text: 'Text', |
| iconHeight: '2ex', |
| iconWidth: '3ex', |
| }, |
| ], |
| }); |
| |
| const icons = extractIconGroups(shadowRoot); |
| assert.lengthOf(icons, 1); |
| const icon = icons[0]; |
| assert.strictEqual(icon.iconData.height, '2ex'); |
| assert.strictEqual(icon.iconData.width, '3ex'); |
| }); |
| |
| describe('data getter and setter', () => { |
| it('renders correctly with two icons', () => { |
| const {component, shadowRoot} = renderIconButton({ |
| clickHandler: () => {}, |
| groups: [ |
| defaultIcon, |
| { |
| iconName: 'warning', |
| iconColor: 'var(--icon-warning)', |
| text: '31', |
| }, |
| ], |
| }); |
| |
| const iconsBefore = extractIconGroups(shadowRoot); |
| assert.lengthOf(iconsBefore, 2); |
| assert.deepEqual(iconsBefore.map(c => c.label), ['1', '31']); |
| const iconNamesBefore = iconsBefore.map(c => 'iconName' in c.iconData ? c.iconData.iconName : undefined); |
| assert.deepEqual(iconNamesBefore, ['cross-circle', 'warning']); |
| |
| const data = component.data; |
| component.data = {...data, groups: data.groups.map((group, index) => ({...group, text: `${index}`}))}; |
| |
| const iconsAfter = extractIconGroups(shadowRoot); |
| assert.lengthOf(iconsAfter, 2); |
| assert.deepEqual(iconsAfter.map(c => c.label), ['0', '1']); |
| const iconNamesAfter = iconsAfter.map(c => 'iconName' in c.iconData ? c.iconData.iconName : undefined); |
| assert.deepEqual(iconNamesAfter, ['cross-circle', 'warning']); |
| }); |
| }); |
| |
| describe('click event', () => { |
| it('is dispatched from button', async () => { |
| let clickHandler: () => void = () => {}; |
| const clicked = new Promise<void>(r => { |
| clickHandler = r; |
| }); |
| const {shadowRoot} = renderIconButton({clickHandler, groups: [defaultIcon]}); |
| const icon = shadowRoot.querySelector('.status-icon'); |
| assert.instanceOf(icon, Icon); |
| icon.click(); |
| await clicked; |
| }); |
| |
| it('is dispatched from child of button', async () => { |
| let clickHandler: () => void = () => {}; |
| const clicked = new Promise<void>(r => { |
| clickHandler = r; |
| }); |
| const {shadowRoot} = renderIconButton({clickHandler, groups: [defaultIcon]}); |
| const icon = shadowRoot.querySelector('.icon-button'); |
| assert.instanceOf(icon, HTMLButtonElement); |
| icon.click(); |
| await clicked; |
| }); |
| }); |
| |
| describe('border', () => { |
| it('is rendered when there is a click handler', async () => { |
| const {shadowRoot} = renderIconButton({clickHandler: () => {}, groups: [defaultIcon]}); |
| const button = shadowRoot.querySelector('.icon-button'); |
| assert.instanceOf(button, HTMLButtonElement); |
| assert.isTrue(button.classList.contains('with-click-handler')); |
| }); |
| |
| it('is omitted when requested', async () => { |
| const {shadowRoot} = renderIconButton({groups: [defaultIcon]}); |
| const button = shadowRoot.querySelector('.icon-button'); |
| assert.instanceOf(button, HTMLButtonElement); |
| assert.isFalse(button.classList.contains('with-click-handler')); |
| }); |
| }); |
| |
| describe('leading text', () => { |
| it('is rendered if provided', async () => { |
| const {shadowRoot} = renderIconButton({clickHandler: () => {}, groups: [defaultIcon], leadingText: 'LEAD'}); |
| const texts = Array.from(shadowRoot.querySelectorAll('.icon-button-title')); |
| assert.deepEqual(texts.map(x => x.textContent), ['LEAD', '1']); |
| }); |
| |
| it('is omitted in compact mode even if provided', async () => { |
| const {shadowRoot} = |
| renderIconButton({clickHandler: () => {}, groups: [defaultIcon], leadingText: 'LEAD', compact: true}); |
| const texts = Array.from(shadowRoot.querySelectorAll('.icon-button-title')); |
| assert.deepEqual(texts.map(x => x.textContent), ['1']); |
| }); |
| |
| it('is omitted if not provided', async () => { |
| const {shadowRoot} = renderIconButton({clickHandler: () => {}, groups: [defaultIcon]}); |
| const texts = Array.from(shadowRoot.querySelectorAll('.icon-button-title')); |
| assert.deepEqual(texts.map(x => x.textContent), ['1']); |
| }); |
| }); |
| |
| describe('trailing text', () => { |
| it('is rendered if provided', async () => { |
| const {shadowRoot} = renderIconButton({clickHandler: () => {}, groups: [defaultIcon], trailingText: 'TRAIL'}); |
| const texts = Array.from(shadowRoot.querySelectorAll('.icon-button-title')); |
| assert.deepEqual(texts.map(x => x.textContent), ['1', 'TRAIL']); |
| }); |
| |
| it('is omitted in compact mode even if provided', async () => { |
| const {shadowRoot} = |
| renderIconButton({clickHandler: () => {}, groups: [defaultIcon], trailingText: 'TRAIL', compact: true}); |
| const texts = Array.from(shadowRoot.querySelectorAll('.icon-button-title')); |
| assert.deepEqual(texts.map(x => x.textContent), ['1']); |
| }); |
| |
| it('is omitted if not provided', async () => { |
| const {shadowRoot} = renderIconButton({clickHandler: () => {}, groups: [defaultIcon]}); |
| const texts = Array.from(shadowRoot.querySelectorAll('.icon-button-title')); |
| assert.deepEqual(texts.map(x => x.textContent), ['1']); |
| }); |
| }); |
| |
| describe('accessible name', () => { |
| it('is rendered if provided', () => { |
| const expectedAccessibleName = 'AccessibleName'; |
| const {shadowRoot} = |
| renderIconButton({clickHandler: () => {}, groups: [defaultIcon], accessibleName: expectedAccessibleName}); |
| const accessibleName = shadowRoot.querySelector('button')!.getAttribute('aria-label'); |
| assert.deepEqual(accessibleName, expectedAccessibleName); |
| }); |
| |
| it('is omitted if not provided', () => { |
| const {shadowRoot} = renderIconButton({clickHandler: () => {}, groups: [defaultIcon]}); |
| const accessibleName = shadowRoot.querySelector('button')!.getAttribute('aria-label'); |
| assert.isNull(accessibleName); |
| }); |
| }); |
| }); |