@spectrum-web-components/tabs 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +201 -0
  2. package/package.json +8 -7
  3. package/stories/index.js +53 -0
  4. package/stories/index.js.map +7 -0
  5. package/stories/tabs-horizontal-sizes.stories.js +91 -0
  6. package/stories/tabs-horizontal-sizes.stories.js.map +7 -0
  7. package/stories/tabs-overflow-panel-sizes.stories.js +35 -0
  8. package/stories/tabs-overflow-panel-sizes.stories.js.map +7 -0
  9. package/stories/tabs-overflow-sizes.stories.js +31 -0
  10. package/stories/tabs-overflow-sizes.stories.js.map +7 -0
  11. package/stories/tabs-overflow.stories.js +37 -0
  12. package/stories/tabs-overflow.stories.js.map +7 -0
  13. package/stories/tabs-vertical-right-sizes.stories.js +88 -0
  14. package/stories/tabs-vertical-right-sizes.stories.js.map +7 -0
  15. package/stories/tabs-vertical-sizes.stories.js +88 -0
  16. package/stories/tabs-vertical-sizes.stories.js.map +7 -0
  17. package/stories/tabs.stories.js +504 -0
  18. package/stories/tabs.stories.js.map +7 -0
  19. package/test/benchmark/basic-test.js +13 -0
  20. package/test/benchmark/basic-test.js.map +7 -0
  21. package/test/tab.test.js +47 -0
  22. package/test/tab.test.js.map +7 -0
  23. package/test/tabs-horizontal-sizes.test-vrt.js +5 -0
  24. package/test/tabs-horizontal-sizes.test-vrt.js.map +7 -0
  25. package/test/tabs-memory.test.js +13 -0
  26. package/test/tabs-memory.test.js.map +7 -0
  27. package/test/tabs-overflow-panel-sizes.test-vrt.js +5 -0
  28. package/test/tabs-overflow-panel-sizes.test-vrt.js.map +7 -0
  29. package/test/tabs-overflow-sizes.test-vrt.js +5 -0
  30. package/test/tabs-overflow-sizes.test-vrt.js.map +7 -0
  31. package/test/tabs-overflow.test-vrt.js +5 -0
  32. package/test/tabs-overflow.test-vrt.js.map +7 -0
  33. package/test/tabs-overflow.test.js +389 -0
  34. package/test/tabs-overflow.test.js.map +7 -0
  35. package/test/tabs-vertical-right-sizes.test-vrt.js +5 -0
  36. package/test/tabs-vertical-right-sizes.test-vrt.js.map +7 -0
  37. package/test/tabs-vertical-sizes.test-vrt.js +5 -0
  38. package/test/tabs-vertical-sizes.test-vrt.js.map +7 -0
  39. package/test/tabs.test-vrt.js +5 -0
  40. package/test/tabs.test-vrt.js.map +7 -0
  41. package/test/tabs.test.js +507 -0
  42. package/test/tabs.test.js.map +7 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["tabs-overflow.test.ts"],
4
+ "sourcesContent": ["/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport { ActionButton } from '@spectrum-web-components/action-button';\nimport { isFirefox } from '@spectrum-web-components/shared/src/platform.js';\nimport {\n calculateScrollTargetForLeftSide,\n calculateScrollTargetForRightSide,\n Tab,\n Tabs,\n TabsOverflow,\n} from '@spectrum-web-components/tabs';\nimport '@spectrum-web-components/tabs/sp-tab-panel.js';\nimport '@spectrum-web-components/tabs/sp-tab.js';\nimport '@spectrum-web-components/tabs/sp-tabs-overflow.js';\nimport '@spectrum-web-components/tabs/sp-tabs.js';\nimport '@spectrum-web-components/theme/scale-medium.js';\nimport '@spectrum-web-components/theme/sp-theme.js';\nimport '@spectrum-web-components/theme/theme-light.js';\n\nimport { elementUpdated, expect, fixture, waitUntil } from '@open-wc/testing';\nimport {\n ElementSize,\n ElementSizes,\n html,\n nothing,\n} from '@spectrum-web-components/base';\nimport { sendKeys, setViewport } from '@web/test-runner-commands';\nimport { repeat } from 'lit/directives/repeat.js';\n\nconst RIGHT_BUTTON_SELECTOR = '.right-scroll';\nconst LEFT_BUTTON_SELECTOR = '.left-scroll';\n\ntype OverflowProperties = {\n count: number;\n size: ElementSize;\n includeTabPanel: boolean;\n selected?: number;\n labelPrev?: string;\n labelNext?: string;\n dir?: 'ltr' | 'rtl';\n};\n\nconst renderTabsOverflow = async ({\n count,\n size,\n includeTabPanel,\n selected = 1,\n dir = 'ltr',\n}: OverflowProperties): Promise<HTMLDivElement> => {\n const theme = await fixture<HTMLDivElement>(html`\n <sp-theme dir=${dir} system=\"spectrum\" scale=\"medium\" color=\"light\">\n <div class=\"container\" style=\"width: 200px; height: 150px;\">\n <sp-tabs-overflow>\n <sp-tabs size=${size} selected=${selected}>\n ${repeat(\n new Array(count),\n (item) => item,\n (_item, index) => html`\n <sp-tab\n label=${`Tab Item ${index + 1}`}\n value=${index + 1}\n ></sp-tab>\n `\n )}\n ${includeTabPanel\n ? html`\n ${repeat(\n new Array(count),\n (item) => item,\n (_item, index) => html`\n <sp-tab-panel value=${index + 1}>\n Content for Tab Item ${index + 1}\n </sp-tab-panel>\n `\n )}\n `\n : nothing}\n </sp-tabs>\n </sp-tabs-overflow>\n </div>\n </sp-theme>\n `);\n await elementUpdated(theme);\n const tabsContainer = theme.querySelector('.container') as HTMLDivElement;\n\n return tabsContainer;\n};\n\ndescribe('TabsOverflow', () => {\n it('loads default tabs-overflow accessibly', async () => {\n const el = await fixture<TabsOverflow>(html`\n <sp-tabs-overflow>\n <sp-tabs size=\"m\" selected=\"1\">\n <sp-tab label=\"Tab Item 1\" value=\"1\"></sp-tab>\n <sp-tab label=\"Tab Item 2\" value=\"2\"></sp-tab>\n <sp-tab-panel value=\"1\">Tab Content 1</sp-tab-panel>\n <sp-tab-panel value=\"2\">Tab Content 2</sp-tab-panel>\n </sp-tabs>\n </sp-tabs-overflow>\n `);\n\n await elementUpdated(el);\n\n await expect(el).to.be.accessible();\n });\n\n it('show render left and right buttons in shadowDom', async () => {\n const el = await renderTabsOverflow({\n count: 20,\n size: ElementSizes.L,\n includeTabPanel: true,\n });\n\n const spTabsOverflows: TabsOverflow = el.querySelector(\n 'sp-tabs-overflow'\n ) as TabsOverflow;\n const rightButton = spTabsOverflows.shadowRoot.querySelector(\n '.right-scroll'\n ) as ActionButton;\n expect(rightButton).to.exist;\n const leftButton = spTabsOverflows.shadowRoot.querySelector(\n '.left-scroll'\n ) as ActionButton;\n expect(leftButton).to.exist;\n });\n\n it('reflect proper sp-tab size', async () => {\n const el = await renderTabsOverflow({\n count: 20,\n size: ElementSizes.M,\n includeTabPanel: true,\n });\n\n const spTabsOverflows: TabsOverflow = el.querySelector(\n 'sp-tabs-overflow'\n ) as TabsOverflow;\n\n expect(spTabsOverflows.getAttribute('size')).to.equal('m');\n });\n\n it('should scroll when the button is clicked', async () => {\n const el = await renderTabsOverflow({\n count: 20,\n size: ElementSizes.L,\n includeTabPanel: true,\n });\n await elementUpdated(el);\n\n const spTabsOverflows: TabsOverflow = el.querySelector(\n 'sp-tabs-overflow'\n ) as TabsOverflow;\n const leftButton = spTabsOverflows.shadowRoot.querySelector(\n '.left-scroll'\n ) as ActionButton;\n\n const rightButton = spTabsOverflows.shadowRoot.querySelector(\n '.right-scroll'\n ) as ActionButton;\n\n leftButton.dispatchEvent(new Event('click', {}));\n\n const tabsEl = spTabsOverflows.querySelector('sp-tab') as Tab;\n const initialLeft = tabsEl.getBoundingClientRect().left;\n rightButton.dispatchEvent(new Event('click', {}));\n await elementUpdated(el);\n rightButton.dispatchEvent(new Event('click', {}));\n await elementUpdated(el);\n rightButton.dispatchEvent(new Event('click', {}));\n await elementUpdated(el);\n const finalLeft = tabsEl.getBoundingClientRect().left;\n expect(finalLeft).to.be.lessThanOrEqual(initialLeft);\n });\n\n it('should scroll up to the last item and back in LTR', async () => {\n // TODO: run on iPhone as per https://github.com/adobe/spectrum-web-components/pull/4722\n // await setUserAgent(\n // 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148'\n // );\n\n // Skipping on Firefox due to timeouts on CI\n if (isFirefox()) return;\n\n const el = await renderTabsOverflow({\n count: 8,\n size: ElementSizes.L,\n includeTabPanel: true,\n dir: 'ltr',\n });\n await elementUpdated(el);\n await setViewport({ width: 360, height: 640 });\n await nextFrame();\n\n const tabsOverflow = el.querySelector(\n 'sp-tabs-overflow'\n ) as TabsOverflow;\n\n expect(tabsOverflow['overflowState'].canScrollLeft).to.be.false;\n expect(tabsOverflow['overflowState'].canScrollRight).to.be.true;\n\n await scrollToEnd(el, RIGHT_BUTTON_SELECTOR, 'ltr');\n\n expect(tabsOverflow['overflowState'].canScrollLeft).to.be.true;\n expect(tabsOverflow['overflowState'].canScrollRight).to.be.false;\n\n await scrollToEnd(el, LEFT_BUTTON_SELECTOR, 'ltr');\n\n expect(tabsOverflow['overflowState'].canScrollLeft).to.be.false;\n expect(tabsOverflow['overflowState'].canScrollRight).to.be.true;\n });\n\n it('should scroll up to the last item and back in RTL', async () => {\n // TODO: run on iPhone as per https://github.com/adobe/spectrum-web-components/pull/4722\n // await setUserAgent(\n // 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148'\n // );\n\n // Skipping on Firefox due to timeouts on CI\n if (isFirefox()) return;\n\n const el = await renderTabsOverflow({\n count: 8,\n size: ElementSizes.L,\n includeTabPanel: true,\n dir: 'rtl',\n });\n await elementUpdated(el);\n await setViewport({ width: 360, height: 640 });\n await nextFrame();\n\n const tabsOverflow = el.querySelector(\n 'sp-tabs-overflow'\n ) as TabsOverflow;\n\n expect(tabsOverflow['overflowState'].canScrollLeft).to.be.true;\n expect(tabsOverflow['overflowState'].canScrollRight).to.be.false;\n\n await scrollToEnd(el, LEFT_BUTTON_SELECTOR, 'rtl');\n\n expect(tabsOverflow['overflowState'].canScrollLeft).to.be.false;\n expect(tabsOverflow['overflowState'].canScrollRight).to.be.true;\n\n await scrollToEnd(el, RIGHT_BUTTON_SELECTOR, 'rtl');\n\n expect(tabsOverflow['overflowState'].canScrollLeft).to.be.true;\n expect(tabsOverflow['overflowState'].canScrollRight).to.be.false;\n });\n\n it('should fail properly if slot is not sp-tabs', async () => {\n const el = await fixture<TabsOverflow>(html`\n <sp-tabs-overflow>\n <div>Some div</div>\n </sp-tabs-overflow>\n `);\n\n await elementUpdated(el);\n const slot = el.shadowRoot.querySelector('slot');\n const slotContent = slot?.assignedElements() || '';\n expect(slotContent[0].toString()).to.not.contains('Tabs');\n });\n\n it('should automatically bring the selected tab into view', async () => {\n const el = await renderTabsOverflow({\n count: 20,\n size: ElementSizes.L,\n includeTabPanel: false,\n selected: 10,\n });\n await elementUpdated(el);\n\n // Grab the list of tabs.\n const tabsEl = el.querySelector('sp-tabs') as Tabs;\n\n // Grab the coordonates of the selected tab.\n let selectedTab = tabsEl.querySelector(\n `[role=\"tab\"][value=\"10\"]`\n ) as Tab;\n expect(selectedTab).to.exist;\n let selectedTabPosition = selectedTab.getBoundingClientRect();\n\n // Selected tab is in the viewport, offset left is greater than 0 and less than the width of the tabs.\n expect(selectedTabPosition.left).to.be.greaterThan(0);\n expect(selectedTabPosition.left).to.be.lessThan(tabsEl.offsetWidth);\n\n // First tab is not in the viewport anymore, its offset left is less than 0.\n const firstTab = tabsEl.querySelector(`[role=\"tab\"][value=\"1\"]`) as Tab;\n const firstTabPosition = firstTab.getBoundingClientRect();\n expect(firstTabPosition.left).to.be.lessThan(0);\n\n // Make the component automatically scroll left by selecting the first tab.\n tabsEl.selected = '1';\n await elementUpdated(tabsEl);\n\n selectedTab = tabsEl.querySelector(`[role=\"tab\"][value=\"1\"]`) as Tab;\n expect(selectedTab).to.exist;\n selectedTabPosition = selectedTab.getBoundingClientRect();\n\n // First tab is in the viewport, offset left is greater than 0 and less than the width of the tabs.\n expect(selectedTabPosition.left).to.be.greaterThan(0);\n expect(selectedTabPosition.left).to.be.lessThan(tabsEl.offsetWidth);\n\n // Tab nr. 10 is not in the viewport anymore.\n const previousSelection = tabsEl.querySelector(\n `[role=\"tab\"][value=\"10\"]`\n ) as Tab;\n const previousSelectionPosition =\n previousSelection.getBoundingClientRect();\n expect(previousSelectionPosition.left).to.be.greaterThan(\n tabsEl.offsetWidth\n );\n });\n\n it('prev and next buttons have default labels', async () => {\n const el = await renderTabsOverflow({\n count: 20,\n size: ElementSizes.M,\n includeTabPanel: true,\n });\n await elementUpdated(el);\n\n const spTabsOverflows: TabsOverflow = el.querySelector(\n 'sp-tabs-overflow'\n ) as TabsOverflow;\n const leftButton = spTabsOverflows.shadowRoot.querySelector(\n '.left-scroll'\n ) as ActionButton;\n const rightButton = spTabsOverflows.shadowRoot.querySelector(\n '.right-scroll'\n ) as ActionButton;\n\n expect(leftButton?.getAttribute('aria-label')).to.equal(\n 'Scroll to previous tabs'\n );\n expect(rightButton?.getAttribute('aria-label')).to.equal(\n 'Scroll to next tabs'\n );\n });\n\n it('prev and next buttons labels overwritten via attributes', async () => {\n const tabsContainer = await fixture<HTMLDivElement>(html`\n <div class=\"container\" style=\"width: 200px; height: 150px;\">\n <sp-tabs-overflow\n label-previous=\"custom label prev\"\n label-next=\"custom label next\"\n >\n <sp-tabs size=${ElementSizes.M} selected=${1}>\n ${repeat(\n new Array(20),\n (item) => item,\n (_item, index) => html`\n <sp-tab\n label=${`Tab Item ${index + 1}`}\n value=${index + 1}\n ></sp-tab>\n `\n )}\n ${repeat(\n new Array(20),\n (item) => item,\n (_item, index) => html`\n <sp-tab-panel value=${index + 1}>\n Content for Tab Item ${index + 1}\n </sp-tab-panel>\n `\n )}\n </sp-tabs>\n </sp-tabs-overflow>\n </div>\n `);\n await elementUpdated(tabsContainer);\n const el = tabsContainer;\n\n const spTabsOverflows: TabsOverflow = el.querySelector(\n 'sp-tabs-overflow'\n ) as TabsOverflow;\n const leftButton = spTabsOverflows.shadowRoot.querySelector(\n '.left-scroll'\n ) as ActionButton;\n const rightButton = spTabsOverflows.shadowRoot.querySelector(\n '.right-scroll'\n ) as ActionButton;\n\n expect(leftButton?.getAttribute('aria-label')).to.equal(\n 'custom label prev'\n );\n expect(rightButton?.getAttribute('aria-label')).to.equal(\n 'custom label next'\n );\n });\n});\n\ndescribe('calculateScrollTargetForRightSide', () => {\n const container = { offsetWidth: 100, scrollLeft: 0 } as HTMLDivElement;\n const tabs = [\n { offsetLeft: 0, offsetWidth: 100 }, // currently selected tab\n { offsetLeft: 100, offsetWidth: 100 },\n { offsetLeft: 200, offsetWidth: 100 },\n ] as Tab[];\n\n it('correctly aligns tab on the right side of the viewport', () => {\n // Where do I need to scroll on the x axis to get the tab at index 2 to be visible?\n expect(\n calculateScrollTargetForRightSide(2, 'ltr', tabs, container)\n ).to.equal(100); // You need to scroll 100px more\n\n // Repeat for RTL\n expect(\n calculateScrollTargetForRightSide(2, 'rtl', tabs, container)\n ).to.equal(0); // You need to scroll at the begining of the scrollable area\n });\n});\n\ndescribe('calculateScrollTargetForLeftSide', () => {\n const container = { offsetWidth: 100, scrollLeft: 200 } as HTMLDivElement;\n const tabs = [\n { offsetLeft: -200, offsetWidth: 100 },\n { offsetLeft: -100, offsetWidth: 100 },\n { offsetLeft: 0, offsetWidth: 100 }, // currently selected tab\n ] as Tab[];\n\n it('correctly aligns tab on the left side of the viewport', () => {\n // Where do I need to scroll on the x axis to get the tab at index 1 to be visible?\n expect(\n calculateScrollTargetForLeftSide(1, 'ltr', tabs, container)\n ).to.equal(-100); // you need to scroll back -100px\n\n // Where do I need to scroll on the x axis to get the first tab to be visible?\n expect(\n calculateScrollTargetForLeftSide(0, 'ltr', tabs, container)\n ).to.equal(0); // you need to scroll to the begining of the scrollable area\n\n // Repeat for RTL\n expect(\n calculateScrollTargetForLeftSide(1, 'rtl', tabs, container)\n ).to.equal(100);\n\n expect(\n calculateScrollTargetForLeftSide(0, 'rtl', tabs, container)\n ).to.equal(0);\n });\n});\n\nasync function repeatScroll(\n options: {\n times: number;\n elementToUpdate: TabsOverflow;\n elementToScroll: HTMLElement;\n distanceToReachInIteration: (iteration: number) => number;\n },\n iteration = 1\n): Promise<void> {\n const {\n times,\n elementToUpdate,\n elementToScroll,\n distanceToReachInIteration,\n } = options;\n if (iteration > times) return;\n\n const distanceToReach = distanceToReachInIteration(iteration);\n\n await sendKeys({ press: 'Enter' });\n await elementUpdated(elementToUpdate);\n await waitUntil(\n () =>\n Math.ceil(Math.abs(elementToScroll.scrollLeft)) -\n Math.abs(distanceToReach) ===\n 0,\n `scroll to ${distanceToReach}`\n );\n return await repeatScroll(options, iteration + 1);\n}\n\nasync function scrollToEnd(\n tabsContainer: HTMLDivElement,\n buttonSelector: string,\n direction: 'ltr' | 'rtl' = 'ltr'\n): Promise<void> {\n const tabs = tabsContainer.querySelector('sp-tabs') as Tabs;\n const tabsList = tabs.shadowRoot!.querySelector('#list') as HTMLElement;\n const tabsOverflow = tabsContainer.querySelector(\n 'sp-tabs-overflow'\n ) as TabsOverflow;\n const button = tabsOverflow.shadowRoot.querySelector(\n buttonSelector\n ) as ActionButton;\n\n const { scrollWidth, clientWidth } = tabsList;\n const distPerScroll = clientWidth * tabsOverflow['scrollFactor'];\n const totalScrollDist = scrollWidth - clientWidth;\n const scrollsToEnd = Math.ceil(totalScrollDist / distPerScroll);\n let distanceToReachInIteration: (iteration: number) => number;\n\n if (direction === 'ltr') {\n distanceToReachInIteration =\n buttonSelector === LEFT_BUTTON_SELECTOR\n ? (iteration: number) =>\n Math.max(totalScrollDist - iteration * distPerScroll, 0)\n : (iteration: number) =>\n Math.min(iteration * distPerScroll, totalScrollDist);\n } else {\n distanceToReachInIteration =\n buttonSelector === LEFT_BUTTON_SELECTOR\n ? (iteration: number) =>\n Math.max(-1 * iteration * distPerScroll, -totalScrollDist)\n : (iteration: number) =>\n -Math.max(totalScrollDist - iteration * distPerScroll, 0);\n }\n\n button.focus();\n return await repeatScroll({\n times: scrollsToEnd,\n elementToUpdate: tabsOverflow,\n elementToScroll: tabsList,\n distanceToReachInIteration,\n });\n}\n\nfunction nextFrame(): Promise<void> {\n return new Promise((res) => requestAnimationFrame(() => res()));\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ import * as stories from "../stories/tabs-vertical-right-sizes.stories.js";
3
+ import { regressVisuals } from "../../../test/visual/test.js";
4
+ regressVisuals("TabsVerticalRightSizesStories", stories);
5
+ //# sourceMappingURL=tabs-vertical-right-sizes.test-vrt.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["tabs-vertical-right-sizes.test-vrt.ts"],
4
+ "sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport * as stories from '../stories/tabs-vertical-right-sizes.stories.js';\nimport { regressVisuals } from '../../../test/visual/test.js';\nimport type { TestsType } from '../../../test/visual/test.js';\n\nregressVisuals('TabsVerticalRightSizesStories', stories as unknown as TestsType);\n"],
5
+ "mappings": ";AAYA,YAAY,aAAa;AACzB,SAAS,sBAAsB;AAG/B,eAAe,iCAAiC,OAA+B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ import * as stories from "../stories/tabs-vertical-sizes.stories.js";
3
+ import { regressVisuals } from "../../../test/visual/test.js";
4
+ regressVisuals("TabsVerticalSizesStories", stories);
5
+ //# sourceMappingURL=tabs-vertical-sizes.test-vrt.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["tabs-vertical-sizes.test-vrt.ts"],
4
+ "sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport * as stories from '../stories/tabs-vertical-sizes.stories.js';\nimport { regressVisuals } from '../../../test/visual/test.js';\nimport type { TestsType } from '../../../test/visual/test.js';\n\nregressVisuals('TabsVerticalSizesStories', stories as unknown as TestsType);\n"],
5
+ "mappings": ";AAYA,YAAY,aAAa;AACzB,SAAS,sBAAsB;AAG/B,eAAe,4BAA4B,OAA+B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ import * as stories from "../stories/tabs.stories.js";
3
+ import { regressVisuals } from "../../../test/visual/test.js";
4
+ regressVisuals("TabsStories", stories);
5
+ //# sourceMappingURL=tabs.test-vrt.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["tabs.test-vrt.ts"],
4
+ "sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport * as stories from '../stories/tabs.stories.js';\nimport { regressVisuals } from '../../../test/visual/test.js';\nimport type { TestsType } from '../../../test/visual/test.js';\n\nregressVisuals('TabsStories', stories as unknown as TestsType);\n"],
5
+ "mappings": ";AAYA,YAAY,aAAa;AACzB,SAAS,sBAAsB;AAG/B,eAAe,eAAe,OAA+B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,507 @@
1
+ "use strict";
2
+ import "@spectrum-web-components/tabs/sp-tabs.js";
3
+ import "@spectrum-web-components/tabs/sp-tab.js";
4
+ import "@spectrum-web-components/tabs/sp-tab-panel.js";
5
+ import { Tab } from "@spectrum-web-components/tabs";
6
+ import "@spectrum-web-components/icons-workflow/icons/sp-icon-checkmark.js";
7
+ import {
8
+ elementUpdated,
9
+ expect,
10
+ fixture,
11
+ nextFrame,
12
+ waitUntil
13
+ } from "@open-wc/testing";
14
+ import { html } from "lit/static-html.js";
15
+ import { LitElement } from "@spectrum-web-components/base";
16
+ import {
17
+ arrowDownEvent,
18
+ arrowLeftEvent,
19
+ arrowRightEvent,
20
+ arrowUpEvent,
21
+ enterEvent,
22
+ spaceEvent
23
+ } from "../../../test/testing-helpers.js";
24
+ import { sendKeys } from "@web/test-runner-commands";
25
+ const createTabs = async () => {
26
+ const tabs = await fixture(
27
+ html`
28
+ <sp-tabs selected="first">
29
+ <sp-tab label="Tab 1" value="first"></sp-tab>
30
+ <sp-tab label="Tab 2" value="second"></sp-tab>
31
+ <sp-tab label="Tab 3" value="third"></sp-tab>
32
+ <sp-tab-panel value="first">First tab content</sp-tab-panel>
33
+ <sp-tab-panel value="second">Second tab content</sp-tab-panel>
34
+ <sp-tab-panel value="third">Third tab content</sp-tab-panel>
35
+ </sp-tabs>
36
+ `
37
+ );
38
+ await elementUpdated(tabs);
39
+ return tabs;
40
+ };
41
+ describe("Tabs", () => {
42
+ it("loads accessibly", async () => {
43
+ const tabs = await createTabs();
44
+ const tabList = tabs.querySelectorAll("sp-tab");
45
+ expect(tabList).to.exist;
46
+ expect(tabList.length).to.equal(3);
47
+ await expect(tabs).to.be.accessible();
48
+ });
49
+ it("loads accessibly w/o panels", async () => {
50
+ const tabs = await fixture(
51
+ html`
52
+ <sp-tabs selected="first">
53
+ <sp-tab value="first">Tab 1</sp-tab>
54
+ <sp-tab value="second">Tab 2</sp-tab>
55
+ <sp-tab value="third">Tab 3</sp-tab>
56
+ </sp-tabs>
57
+ `
58
+ );
59
+ const tabList = tabs.querySelectorAll("sp-tab");
60
+ expect(tabList).to.exist;
61
+ expect(tabList.length).to.equal(3);
62
+ await expect(tabs).to.be.accessible();
63
+ });
64
+ it("can be disabled", async () => {
65
+ const tabs = await createTabs();
66
+ const tab = tabs.querySelector('[label="Tab 3"]');
67
+ tabs.disabled = true;
68
+ await elementUpdated(tabs);
69
+ expect(tabs.selected).to.equal("first");
70
+ tab.click();
71
+ await elementUpdated(tabs);
72
+ expect(tabs.selected).to.equal("first");
73
+ });
74
+ it("can have disabled sp-tab children", async () => {
75
+ const tabs = await createTabs();
76
+ const tab2 = tabs.querySelector('[label="Tab 2"]');
77
+ const tab3 = tabs.querySelector('[label="Tab 3"]');
78
+ tab3.disabled = true;
79
+ await elementUpdated(tab3);
80
+ expect(tabs.selected).to.equal("first");
81
+ tab3.click();
82
+ await elementUpdated(tabs);
83
+ expect(tabs.selected).to.equal("first");
84
+ tab2.click();
85
+ await elementUpdated(tabs);
86
+ expect(tabs.selected).to.equal("second");
87
+ });
88
+ it("reflects selected tab with selected property", async () => {
89
+ const tabs = await createTabs();
90
+ const firstTab = tabs.querySelector("sp-tab[value=first]");
91
+ const secondTab = tabs.querySelector("sp-tab[value=second]");
92
+ const thirdTab = tabs.querySelector("sp-tab[value=third]");
93
+ const firstPanel = tabs.querySelector(
94
+ "sp-tab-panel[value=first]"
95
+ );
96
+ const secondPanel = tabs.querySelector(
97
+ "sp-tab-panel[value=second]"
98
+ );
99
+ const thirdPanel = tabs.querySelector(
100
+ "sp-tab-panel[value=third]"
101
+ );
102
+ expect(firstTab.selected, "first: 1, selected").to.be.true;
103
+ expect(firstPanel.selected, "first panel: 1, selected").to.be.true;
104
+ expect(secondTab.selected, "second: 1, not selected").to.be.false;
105
+ expect(secondPanel.selected, "second panel: 1, not selected").to.be.false;
106
+ expect(thirdTab.selected, "third: 1, not selected").to.be.false;
107
+ expect(thirdPanel.selected, "third panel: 1, not selected").to.be.false;
108
+ expect(tabs.selected).to.equal(firstTab.value);
109
+ secondTab.click();
110
+ await elementUpdated(tabs);
111
+ expect(firstTab.selected, "first: 2, not selected").to.be.false;
112
+ expect(firstPanel.selected, "first panel: 2, not selected").to.be.false;
113
+ expect(secondTab.selected, "second: 2, selected").to.be.true;
114
+ expect(secondTab.selected, "first panel: 2, selected").to.be.true;
115
+ expect(thirdTab.selected, "third: 2, not selected").to.be.false;
116
+ expect(thirdTab.selected, "first panel: 2, not selected").to.be.false;
117
+ expect(tabs.selected).to.equal(secondTab.value);
118
+ thirdTab.click();
119
+ await elementUpdated(tabs);
120
+ expect(firstTab.selected, "first: 3, not selected").to.be.false;
121
+ expect(firstPanel.selected, "first panel: 3, not selected").to.be.false;
122
+ expect(secondTab.selected, "second: 3, not selected").to.be.false;
123
+ expect(secondPanel.selected, "second panel: 3, not selected").to.be.false;
124
+ expect(thirdTab.selected, "third: 3, selected").to.be.true;
125
+ expect(thirdTab.selected, "first panel: 3, selected").to.be.true;
126
+ expect(tabs.selected).to.equal(thirdTab.value);
127
+ });
128
+ it("autofocuses", async () => {
129
+ const tabs = await fixture(
130
+ html`
131
+ <sp-tabs selected="second" autofocus>
132
+ <sp-tab label="Tab 1" value="first"></sp-tab>
133
+ <sp-tab label="Tab 2" value="second"></sp-tab>
134
+ <sp-tab label="Tab 3" value="third"></sp-tab>
135
+ </sp-tabs>
136
+ `
137
+ );
138
+ await elementUpdated(tabs);
139
+ const autoElement = tabs.querySelector('[label="Tab 2"]');
140
+ await waitUntil(
141
+ () => document.activeElement === autoElement,
142
+ "Autofocused"
143
+ );
144
+ });
145
+ it("auto", async () => {
146
+ const el = await fixture(
147
+ html`
148
+ <sp-tabs selected="second" auto>
149
+ <sp-tab label="Tab 1" value="first"></sp-tab>
150
+ <sp-tab label="Tab 2" value="second"></sp-tab>
151
+ <sp-tab label="Tab 3" value="third"></sp-tab>
152
+ </sp-tabs>
153
+ `
154
+ );
155
+ await elementUpdated(el);
156
+ expect(el.selected).to.equal("second");
157
+ el.focus();
158
+ await sendKeys({
159
+ press: "ArrowLeft"
160
+ });
161
+ expect(el.selected).to.equal("first");
162
+ await sendKeys({
163
+ press: "ArrowLeft"
164
+ });
165
+ expect(el.selected).to.equal("third");
166
+ await sendKeys({
167
+ press: "ArrowRight"
168
+ });
169
+ expect(el.selected).to.equal("first");
170
+ });
171
+ it("forces only one tab to be selected", async () => {
172
+ const tabs = await createTabs();
173
+ const selectedTabs = tabs.querySelectorAll("sp-tab[selected]");
174
+ expect(tabs.selected).to.equal("first");
175
+ expect(selectedTabs.length).to.equal(1);
176
+ });
177
+ it("de-selects all but first selected tab if multiple selected", async () => {
178
+ const tabs = await createTabs();
179
+ const tab1 = tabs.querySelector("sp-tab[value=first]");
180
+ const tab2 = tabs.querySelector("sp-tab[value=second]");
181
+ if (!(tab1 instanceof Tab)) throw new Error("tab1 not of type Tab");
182
+ if (!(tab2 instanceof Tab)) throw new Error("tab2 not of type Tab");
183
+ expect(tabs.selected).to.equal("first");
184
+ expect(tab1.selected).to.be.true;
185
+ expect(tab2.selected).to.be.false;
186
+ });
187
+ it("ensures setting selection updates selected tab", async () => {
188
+ const tabs = await createTabs();
189
+ const tab1 = tabs.querySelector("sp-tab[value=first]");
190
+ const tab2 = tabs.querySelector("sp-tab[value=second]");
191
+ const tab3 = tabs.querySelector("sp-tab[value=third]");
192
+ if (!(tab1 instanceof Tab)) throw new Error("tab1 not of type Tab");
193
+ if (!(tab2 instanceof Tab)) throw new Error("tab2 not of type Tab");
194
+ if (!(tab3 instanceof Tab)) throw new Error("tab3 not of type Tab");
195
+ expect(tabs.selected).to.equal("first");
196
+ expect(tab1.selected, "first: 1, selected").to.be.true;
197
+ expect(tab2.selected, "second: 1, not selected").to.be.false;
198
+ expect(tab3.selected, "thurd: 1, not selected").to.be.false;
199
+ tabs.selected = "second";
200
+ await elementUpdated(tabs);
201
+ expect(tabs.selected).to.equal("second");
202
+ expect(tab1.selected, "first: 2, not selected").to.be.false;
203
+ expect(tab2.selected, "second: 2, selected").to.be.true;
204
+ expect(tab3.selected, "third: 2, not selected").to.be.false;
205
+ tabs.selected = "third";
206
+ await elementUpdated(tabs);
207
+ expect(tabs.selected).to.equal("third");
208
+ expect(tab1.selected, "first: 3, not selected").to.be.false;
209
+ expect(tab2.selected, "second: 3, not selected").to.be.false;
210
+ expect(tab3.selected, "third: 3, selected").to.be.true;
211
+ });
212
+ it("ensures setting selected and clicking on tab both work together", async () => {
213
+ const tabs = await createTabs();
214
+ const tab1 = tabs.querySelector("sp-tab[value=first]");
215
+ const tab2 = tabs.querySelector("sp-tab[value=second]");
216
+ const tab3 = tabs.querySelector("sp-tab[value=third]");
217
+ if (!(tab1 instanceof Tab)) throw new Error("tab1 not of type Tab");
218
+ if (!(tab2 instanceof Tab)) throw new Error("tab2 not of type Tab");
219
+ if (!(tab3 instanceof Tab)) throw new Error("tab3 not of type Tab");
220
+ tab2.click();
221
+ await elementUpdated(tabs);
222
+ expect(tabs.selected).to.equal("second");
223
+ expect(tab1.selected).to.be.false;
224
+ expect(tab2.selected).to.be.true;
225
+ expect(tab3.selected).to.be.false;
226
+ tabs.selected = "first";
227
+ await elementUpdated(tabs);
228
+ expect(tabs.selected).to.equal("first");
229
+ expect(tab1.selected).to.be.true;
230
+ expect(tab2.selected).to.be.false;
231
+ expect(tab3.selected).to.be.false;
232
+ });
233
+ it("displays `vertical`", async () => {
234
+ const el = await fixture(html`
235
+ <sp-tabs selected="first" direction="vertical">
236
+ <sp-tab label="Tab 1" value="first"></sp-tab>
237
+ <sp-tab label="Tab 2" value="second"></sp-tab>
238
+ <sp-tab label="Tab 3" value="third"></sp-tab>
239
+ </sp-tabs>
240
+ `);
241
+ await elementUpdated(el);
242
+ expect(el.selected).to.be.equal("first");
243
+ el.selected = "first";
244
+ await elementUpdated(el);
245
+ expect(el.selected).to.be.equal("first");
246
+ });
247
+ it("displays with nothing `selected`", async () => {
248
+ const el = await fixture(html`
249
+ <sp-tabs>
250
+ <sp-tab label="Tab 1" value="first"></sp-tab>
251
+ <sp-tab label="Tab 2" value="second"></sp-tab>
252
+ <sp-tab label="Tab 3" value="third"></sp-tab>
253
+ </sp-tabs>
254
+ `);
255
+ await elementUpdated(el);
256
+ expect(el.selected).to.be.equal("");
257
+ el.selected = "first";
258
+ await elementUpdated(el);
259
+ expect(el.selected).to.be.equal("first");
260
+ });
261
+ it("ignores children with no `value`", async () => {
262
+ const el = await fixture(html`
263
+ <sp-tabs selected="first">
264
+ <sp-tab label="Tab 1" value="first"></sp-tab>
265
+ <div id="other">Other thing</div>
266
+ </sp-tabs>
267
+ `);
268
+ await elementUpdated(el);
269
+ expect(el.selected).to.be.equal("first");
270
+ const otherThing = el.querySelector("#other");
271
+ otherThing.click();
272
+ await elementUpdated(el);
273
+ expect(el.selected).to.be.equal("first");
274
+ });
275
+ it("allows selection to be cancellable", async () => {
276
+ const cancelSelection = (event) => event.preventDefault();
277
+ const el = await fixture(html`
278
+ <sp-tabs selected="first" @change=${cancelSelection}>
279
+ <sp-tab label="Tab 1" value="first"></sp-tab>
280
+ <sp-tab label="Tab 2" value="second"></sp-tab>
281
+ </sp-tabs>
282
+ `);
283
+ await elementUpdated(el);
284
+ expect(el.selected).to.be.equal("first");
285
+ const secondTab = el.querySelector('[value="second"]');
286
+ secondTab.click();
287
+ await elementUpdated(el);
288
+ expect(el.selected).to.be.equal("first");
289
+ });
290
+ it("prevents [tabindex=0] while `focusin`", async () => {
291
+ const el = await fixture(html`
292
+ <sp-tabs>
293
+ <sp-tab label="Tab 1" value="first" selected>
294
+ <sp-icon-checkmark slot="icon"></sp-icon-checkmark>
295
+ </sp-tab>
296
+ <sp-tab label="Tab 2" value="second">
297
+ <sp-icon-checkmark slot="icon"></sp-icon-checkmark>
298
+ </sp-tab>
299
+ </sp-tabs>
300
+ `);
301
+ const selected = el.querySelector('[value="first"]');
302
+ const toBeSelected = el.querySelector('[value="second"]');
303
+ await elementUpdated(el);
304
+ await waitUntil(() => el.selected === "first", "wait for selection");
305
+ expect(el.selected).to.equal("first");
306
+ expect(selected.tabIndex).to.equal(0);
307
+ toBeSelected.dispatchEvent(new Event("focusin", { bubbles: true }));
308
+ await elementUpdated(el);
309
+ expect(el.selected).to.equal("first");
310
+ expect(selected.tabIndex).to.equal(-1);
311
+ toBeSelected.dispatchEvent(new Event("focusout", { bubbles: true }));
312
+ await elementUpdated(el);
313
+ expect(el.selected).to.equal("first");
314
+ expect(selected.tabIndex).to.equal(0);
315
+ toBeSelected.click();
316
+ await elementUpdated(el);
317
+ expect(el.selected).to.equal("second");
318
+ expect(toBeSelected.tabIndex).to.equal(0);
319
+ });
320
+ it("accepts keyboard based selection", async () => {
321
+ const el = await fixture(html`
322
+ <sp-tabs selected="Unknown">
323
+ <sp-tab label="Tab 1" value="first">
324
+ <sp-icon-checkmark slot="icon"></sp-icon-checkmark>
325
+ </sp-tab>
326
+ <sp-tab label="Tab 2" value="second">
327
+ <sp-icon-checkmark slot="icon"></sp-icon-checkmark>
328
+ </sp-tab>
329
+ </sp-tabs>
330
+ `);
331
+ await elementUpdated(el);
332
+ expect(el.selected).to.be.equal("");
333
+ const firstTab = el.querySelector('[value="first"]');
334
+ const secondTab = el.querySelector('[value="second"]');
335
+ firstTab.dispatchEvent(new FocusEvent("focusin", { bubbles: true }));
336
+ firstTab.focus();
337
+ await elementUpdated(el);
338
+ expect(document.activeElement === firstTab, "Focus first tab").to.be.true;
339
+ firstTab.dispatchEvent(arrowLeftEvent());
340
+ await elementUpdated(el);
341
+ expect(document.activeElement === secondTab, "Focus second tab").to.be.true;
342
+ secondTab.dispatchEvent(enterEvent());
343
+ await elementUpdated(el);
344
+ expect(el.selected).to.be.equal("second");
345
+ secondTab.dispatchEvent(arrowRightEvent());
346
+ await elementUpdated(el);
347
+ expect(document.activeElement === firstTab, "Focus first tab").to.be.true;
348
+ firstTab.dispatchEvent(spaceEvent());
349
+ await elementUpdated(el);
350
+ expect(el.selected).to.be.equal("first");
351
+ firstTab.dispatchEvent(arrowUpEvent());
352
+ await elementUpdated(el);
353
+ expect(document.activeElement === secondTab, "Focus second tab").to.be.true;
354
+ secondTab.dispatchEvent(arrowDownEvent());
355
+ await elementUpdated(el);
356
+ expect(document.activeElement === firstTab, "Focus first tab").to.be.true;
357
+ });
358
+ it("accepts keyboard based selection through shadow DOM", async () => {
359
+ class TabTestEl extends LitElement {
360
+ render() {
361
+ return html`
362
+ <sp-tabs selected="Unknown">
363
+ <sp-tab label="Tab 1" value="first">
364
+ <sp-icon-checkmark slot="icon"></sp-icon-checkmark>
365
+ </sp-tab>
366
+ <sp-tab label="Tab 2" value="second">
367
+ <sp-icon-checkmark slot="icon"></sp-icon-checkmark>
368
+ </sp-tab>
369
+ </sp-tabs>
370
+ `;
371
+ }
372
+ }
373
+ customElements.define("tab-test-el", TabTestEl);
374
+ const el = await fixture(
375
+ html`
376
+ <tab-test-el></tab-test-el>
377
+ `
378
+ );
379
+ await elementUpdated(el);
380
+ const rootNode = el.shadowRoot;
381
+ const tabsEl = rootNode.querySelector("sp-tabs");
382
+ await elementUpdated(tabsEl);
383
+ expect(tabsEl.selected).to.be.equal("");
384
+ const firstTab = tabsEl.querySelector('[value="first"]');
385
+ const secondTab = tabsEl.querySelector('[value="second"]');
386
+ firstTab.dispatchEvent(new FocusEvent("focusin", { bubbles: true }));
387
+ firstTab.focus();
388
+ await elementUpdated(el);
389
+ let activeElement = rootNode.activeElement;
390
+ expect(activeElement === firstTab, "Focus first tab").to.be.true;
391
+ firstTab.dispatchEvent(arrowLeftEvent());
392
+ await elementUpdated(el);
393
+ activeElement = rootNode.activeElement;
394
+ expect(activeElement === secondTab, "Focus second tab").to.be.true;
395
+ secondTab.dispatchEvent(enterEvent());
396
+ await elementUpdated(el);
397
+ expect(tabsEl.selected).to.be.equal("second");
398
+ secondTab.dispatchEvent(arrowRightEvent());
399
+ await elementUpdated(el);
400
+ activeElement = rootNode.activeElement;
401
+ expect(activeElement === firstTab, "Focus first tab").to.be.true;
402
+ firstTab.dispatchEvent(spaceEvent());
403
+ await elementUpdated(el);
404
+ expect(tabsEl.selected).to.be.equal("first");
405
+ });
406
+ it('accepts keyboard based selection - [direction="vertical"]', async () => {
407
+ const el = await fixture(html`
408
+ <sp-tabs selected="Unknown" direction="vertical">
409
+ <sp-tab label="Tab 1" value="first">
410
+ <sp-icon-checkmark slot="icon"></sp-icon-checkmark>
411
+ </sp-tab>
412
+ <sp-tab label="Tab 2" value="second">
413
+ <sp-icon-checkmark slot="icon"></sp-icon-checkmark>
414
+ </sp-tab>
415
+ </sp-tabs>
416
+ `);
417
+ await elementUpdated(el);
418
+ expect(el.selected).to.be.equal("");
419
+ const firstTab = el.querySelector('[value="first"]');
420
+ const secondTab = el.querySelector('[value="second"]');
421
+ firstTab.dispatchEvent(new FocusEvent("focusin", { bubbles: true }));
422
+ firstTab.focus();
423
+ await elementUpdated(el);
424
+ expect(document.activeElement === firstTab, "Focus first tab").to.be.true;
425
+ firstTab.dispatchEvent(arrowLeftEvent());
426
+ await elementUpdated(el);
427
+ expect(document.activeElement === secondTab, "Focus second tab").to.be.true;
428
+ secondTab.dispatchEvent(enterEvent());
429
+ await elementUpdated(el);
430
+ expect(el.selected).to.be.equal("second");
431
+ secondTab.dispatchEvent(arrowDownEvent());
432
+ await elementUpdated(el);
433
+ expect(document.activeElement === firstTab, "Focus first tab").to.be.true;
434
+ firstTab.dispatchEvent(spaceEvent());
435
+ await elementUpdated(el);
436
+ expect(el.selected).to.be.equal("first");
437
+ firstTab.dispatchEvent(arrowRightEvent());
438
+ await elementUpdated(el);
439
+ expect(document.activeElement === secondTab, "Focus second tab").to.be.true;
440
+ secondTab.dispatchEvent(arrowLeftEvent());
441
+ expect(document.activeElement === firstTab, "Focus first tab").to.be.true;
442
+ });
443
+ it("selects through slotted DOM", async () => {
444
+ const el = await fixture(
445
+ html`
446
+ <sp-tabs selected="first">
447
+ <sp-tab value="first">Tab 1</sp-tab>
448
+ <sp-tab value="second"><span>Tab 2</span></sp-tab>
449
+ </sp-tabs>
450
+ `
451
+ );
452
+ const span = el.querySelector("span");
453
+ await elementUpdated(el);
454
+ expect(el.selected).to.equal("first");
455
+ span.click();
456
+ await elementUpdated(el);
457
+ expect(el.selected).to.equal("second");
458
+ });
459
+ it("updates selection indicator in response to tab updates", async () => {
460
+ const el = await fixture(
461
+ html`
462
+ <sp-tabs selected="first">
463
+ <sp-tab value="first">Tab 1</sp-tab>
464
+ <sp-tab value="second">Tab 2</sp-tab>
465
+ </sp-tabs>
466
+ `
467
+ );
468
+ const selected = el.querySelector('[value="first"]');
469
+ await elementUpdated(el);
470
+ const extractScaleX = /scaleX\((.+)\)/;
471
+ const initialExec = extractScaleX.exec(
472
+ el.selectionIndicatorStyle
473
+ );
474
+ const initialWidth = parseFloat(initialExec[1]);
475
+ selected.textContent = "WWWWWWWWWWWWWWWWWWWWWWWWW";
476
+ await nextFrame();
477
+ await nextFrame();
478
+ const longerExec = extractScaleX.exec(
479
+ el.selectionIndicatorStyle
480
+ );
481
+ const longerWidth = parseFloat(longerExec[1]);
482
+ expect(initialWidth).to.be.lessThan(longerWidth);
483
+ selected.textContent = "W";
484
+ await nextFrame();
485
+ const shorterExec = extractScaleX.exec(
486
+ el.selectionIndicatorStyle
487
+ );
488
+ const shorterWidth = parseFloat(shorterExec[1]);
489
+ expect(initialWidth).to.be.greaterThan(shorterWidth);
490
+ expect(longerWidth).to.be.greaterThan(shorterWidth);
491
+ });
492
+ it("clicks on #list do not throw", async () => {
493
+ const tabs = await createTabs();
494
+ const tabList = tabs.shadowRoot.querySelector(
495
+ "#list"
496
+ );
497
+ let hasError = false;
498
+ const oldOnerror = window.onerror;
499
+ window.onerror = () => {
500
+ hasError = true;
501
+ };
502
+ tabList.dispatchEvent(new MouseEvent("click"));
503
+ expect(hasError, "it should not error").to.be.false;
504
+ window.onerror = oldOnerror;
505
+ });
506
+ });
507
+ //# sourceMappingURL=tabs.test.js.map