From e21b08bffe8d637e3e904738ba29eb05bde0d68e Mon Sep 17 00:00:00 2001 From: Kitty Hurley Date: Wed, 30 Aug 2023 13:55:27 -0500 Subject: [PATCH 01/35] docs(accordion-item): update component description for vscode (#7611) **Related Issue:** #6312 ## Summary - Adds context to VS Code for the `accordion-item` component - Tests our maintenance limitation when additional labels are present --- .../calcite-components/src/components/accordion-item/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/calcite-components/src/components/accordion-item/readme.md b/packages/calcite-components/src/components/accordion-item/readme.md index 95dbab18387..c498ed3c1f5 100644 --- a/packages/calcite-components/src/components/accordion-item/readme.md +++ b/packages/calcite-components/src/components/accordion-item/readme.md @@ -1,6 +1,6 @@ # calcite-accordion-item -individual `calcite-accordion` item +A child component of `calcite-accordion`, where some behavior is inherited from its parent such as the `appearance` and `selectionMode`. From 1b3ad081d254dedafaef3ca972aabe2964fd2694 Mon Sep 17 00:00:00 2001 From: Kitty Hurley Date: Wed, 30 Aug 2023 14:28:10 -0500 Subject: [PATCH 02/35] docs: add homepage to npm packages (#7628) **Related Issue:** #7625 ## Summary Adds the [project homepage](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#homepage) to our `calcite-components` and `calcite-components-react` packages. Similar to the `@arcgis/core`'s page: ```json "name": "@arcgis/core", "version": "4.27.6", "homepage": "https://js.arcgis.com", "description": "ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API", ``` ![image](https://github.com/Esri/calcite-design-system/assets/5023024/a46dbbf9-acbc-4292-9ca8-c3cdd607a9b1) --- packages/calcite-components-react/package.json | 1 + packages/calcite-components/package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index e11d67cf6ec..e4d43b98140 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -2,6 +2,7 @@ "name": "@esri/calcite-components-react", "sideEffects": false, "version": "1.7.0-next.13", + "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", "scripts": { diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index 45f32d2ea6e..2a1d537e911 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,7 @@ { "name": "@esri/calcite-components", "version": "1.7.0-next.13", + "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", "module": "dist/index.js", From f413b61b90320214cf09d30ccfe8c256d6b82931 Mon Sep 17 00:00:00 2001 From: Kitty Hurley Date: Wed, 30 Aug 2023 18:18:14 -0500 Subject: [PATCH 03/35] docs(shell): update sheets slot position (#7629) **Related Issue:** https://github.com/Esri/calcite-design-system/issues/7154 ## Summary Doc copyediting for the `shell`'s new `"sheets"` slot. --- packages/calcite-components/src/components/shell/shell.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/calcite-components/src/components/shell/shell.tsx b/packages/calcite-components/src/components/shell/shell.tsx index c8567dfa8e0..78b42b73685 100755 --- a/packages/calcite-components/src/components/shell/shell.tsx +++ b/packages/calcite-components/src/components/shell/shell.tsx @@ -18,7 +18,7 @@ import { CSS, SLOTS } from "./resources"; * @slot center-row - [Deprecated] Use the `"panel-bottom"` slot instead. A slot for adding the bottom `calcite-shell-center-row`. * @slot modals - A slot for adding `calcite-modal` components. When placed in this slot, the modal position will be constrained to the extent of the shell. * @slot alerts - A slot for adding `calcite-alert` components. When placed in this slot, the alert position will be constrained to the extent of the shell. - * @slot sheets - A slot for adding `calcite-sheet` components. When placed in this slot, the alert position will be constrained to the extent of the shell. + * @slot sheets - A slot for adding `calcite-sheet` components. When placed in this slot, the sheet position will be constrained to the extent of the shell. */ @Component({ From c2bf34b69c6c199c4709774b2d80b71d3b2f9b9e Mon Sep 17 00:00:00 2001 From: Erik Harper Date: Wed, 30 Aug 2023 18:49:22 -0700 Subject: [PATCH 04/35] feat(input-time-picker): support fractional seconds (#7532) **Related Issue:** #6591 ## Summary This PR adds initial support for fractional seconds in `input-time-picker` and `time-picker` components. Both `input-time-picker` and `time-picker` officially support fractional step values from `.001` to `.9`. --------- Co-authored-by: Erik Harper --- .../input-time-picker.stories.ts | 42 +++ .../input-time-picker/input-time-picker.tsx | 175 ++++++++-- .../input-time-picker/usage/Basic.md | 8 +- .../usage/Fractional-seconds.md | 3 + .../assets/time-picker/t9n/messages.json | 3 + .../assets/time-picker/t9n/messages_en.json | 3 + .../src/components/time-picker/resources.ts | 3 + .../components/time-picker/time-picker.e2e.ts | 45 +++ .../time-picker/time-picker.stories.ts | 29 ++ .../components/time-picker/time-picker.tsx | 326 +++++++++++++----- .../src/demos/input-time-picker.html | 19 + .../calcite-components/src/utils/math.spec.ts | 26 ++ packages/calcite-components/src/utils/math.ts | 20 +- .../calcite-components/src/utils/time.spec.ts | 196 ++++++++++- packages/calcite-components/src/utils/time.ts | 158 +++++++-- 15 files changed, 912 insertions(+), 144 deletions(-) create mode 100644 packages/calcite-components/src/components/input-time-picker/usage/Fractional-seconds.md create mode 100644 packages/calcite-components/src/components/time-picker/time-picker.stories.ts diff --git a/packages/calcite-components/src/components/input-time-picker/input-time-picker.stories.ts b/packages/calcite-components/src/components/input-time-picker/input-time-picker.stories.ts index c58275bb4c2..2d462c6f2f5 100644 --- a/packages/calcite-components/src/components/input-time-picker/input-time-picker.stories.ts +++ b/packages/calcite-components/src/components/input-time-picker/input-time-picker.stories.ts @@ -26,6 +26,48 @@ export const simple = (): string => html` `; +export const deciSeconds_TestOnly = (): string => html` + + +`; + +export const centiseconds_TestOnly = (): string => html` + + +`; + +export const milliseconds_TestOnly = (): string => html` + + +`; + export const disabled_TestOnly = (): string => html``; diff --git a/packages/calcite-components/src/components/input-time-picker/input-time-picker.tsx b/packages/calcite-components/src/components/input-time-picker/input-time-picker.tsx index 20649d58ed0..85308abd210 100644 --- a/packages/calcite-components/src/components/input-time-picker/input-time-picker.tsx +++ b/packages/calcite-components/src/components/input-time-picker/input-time-picker.tsx @@ -50,7 +50,9 @@ import { } from "../../utils/focusTrapComponent"; import { FocusTrap } from "focus-trap"; import { + formatTimePart, formatTimeString, + FractionalSecondDigits, isValidTime, localizeTimeString, toISOTimeString, @@ -68,9 +70,10 @@ import localizedFormat from "dayjs/esm/plugin/localizedFormat"; import preParsePostFormat from "dayjs/esm/plugin/preParsePostFormat"; import updateLocale from "dayjs/esm/plugin/updateLocale"; import { getSupportedLocale } from "../../utils/locale"; +import { decimalPlaces } from "../../utils/math"; // some bundlers (e.g., Webpack) need dynamic import paths to be static -const supportedDayJsLocaleToLocaleConfigImport = new Map([ +const supportedDayjsLocaleToLocaleConfigImport = new Map([ ["ar", () => import("dayjs/esm/locale/ar.js")], ["bg", () => import("dayjs/esm/locale/bg.js")], ["bs", () => import("dayjs/esm/locale/bs.js")], @@ -129,6 +132,13 @@ dayjs.extend(localizedFormat); dayjs.extend(preParsePostFormat); dayjs.extend(updateLocale); +interface DayjsTimeParts { + hour: number; + minute: number; + second: number; + millisecond: number; +} + @Component({ tag: "calcite-input-time-picker", styleUrl: "input-time-picker.scss", @@ -244,6 +254,7 @@ export class InputTimePicker locale: this.effectiveLocale, numberingSystem, includeSeconds: this.shouldIncludeSeconds(), + fractionalSecondDigits: decimalPlaces(this.step) as FractionalSecondDigits, }) ); } @@ -323,6 +334,8 @@ export class InputTimePicker private dialogId = `time-picker-dialog--${guid()}`; + private localeConfig: ILocale; + /** whether the value of the input was changed as a result of user typing or not */ private userChangedValue = false; @@ -347,6 +360,7 @@ export class InputTimePicker locale, numberingSystem: this.numberingSystem, includeSeconds: this.shouldIncludeSeconds(), + fractionalSecondDigits: decimalPlaces(this.step) as FractionalSecondDigits, }) ); } @@ -386,6 +400,7 @@ export class InputTimePicker locale: this.effectiveLocale, numberingSystem: this.numberingSystem, includeSeconds: this.shouldIncludeSeconds(), + fractionalSecondDigits: decimalPlaces(this.step) as FractionalSecondDigits, }); if (localizedTimeString !== inputValue) { @@ -439,6 +454,7 @@ export class InputTimePicker locale: this.effectiveLocale, numberingSystem: this.numberingSystem, includeSeconds, + fractionalSecondDigits: decimalPlaces(this.step) as FractionalSecondDigits, }) ); }; @@ -476,20 +492,106 @@ export class InputTimePicker // we need to set the corresponding locale before parsing, otherwise it defaults to English (possible dayjs bug) dayjs.locale(this.effectiveLocale.toLowerCase()); - const dayjsParseResult = dayjs(value, ["LTS", "LT"]); + const nonFractionalSecondParts = this.delocalizeTimeStringToParts(value); + + let delocalizedTimeString; + + if (this.shouldIncludeFractionalSeconds()) { + const stepPrecision = decimalPlaces(this.step); + const centisecondParts = this.delocalizeTimeStringToParts(value, "S"); + + if (stepPrecision === 1) { + delocalizedTimeString = + centisecondParts.millisecond !== 0 + ? this.getTimeStringFromParts(centisecondParts) + : this.getTimeStringFromParts(nonFractionalSecondParts); + } else { + const decisecondParts = this.delocalizeTimeStringToParts(value, "SS"); + + if (stepPrecision === 2) { + if (decisecondParts.millisecond !== 0) { + delocalizedTimeString = this.getTimeStringFromParts(decisecondParts); + } else if (centisecondParts.millisecond !== 0) { + delocalizedTimeString = this.getTimeStringFromParts(centisecondParts); + } else { + delocalizedTimeString = this.getTimeStringFromParts(nonFractionalSecondParts); + } + } else if (stepPrecision >= 3) { + const millisecondParts = this.delocalizeTimeStringToParts(value, "SSS"); + + if (millisecondParts.millisecond !== 0) { + delocalizedTimeString = this.getTimeStringFromParts(millisecondParts); + } else if (decisecondParts.millisecond !== 0) { + delocalizedTimeString = this.getTimeStringFromParts(decisecondParts); + } else if (centisecondParts.millisecond !== 0) { + delocalizedTimeString = this.getTimeStringFromParts(centisecondParts); + } else { + delocalizedTimeString = this.getTimeStringFromParts(nonFractionalSecondParts); + } + } + } + } else { + delocalizedTimeString = this.getTimeStringFromParts(nonFractionalSecondParts); + } + + return delocalizedTimeString; + } + + private delocalizeTimeStringToParts( + localizedTimeString: string, + fractionalSecondFormatToken?: "S" | "SS" | "SSS" + ): DayjsTimeParts { + const ltsFormatString = this.localeConfig?.formats?.LTS; + const fractionalSecondTokenMatch = ltsFormatString.match(/ss\.*(S+)/g); + + if (fractionalSecondFormatToken && this.shouldIncludeFractionalSeconds()) { + const secondFormatToken = `ss.${fractionalSecondFormatToken}`; + this.localeConfig.formats.LTS = fractionalSecondTokenMatch + ? ltsFormatString.replace(fractionalSecondTokenMatch[0], secondFormatToken) + : ltsFormatString.replace("ss", secondFormatToken); + } else if (fractionalSecondTokenMatch) { + this.localeConfig.formats.LTS = ltsFormatString.replace(fractionalSecondTokenMatch[0], "ss"); + } + + dayjs.updateLocale( + this.getSupportedDayjsLocale(getSupportedLocale(this.effectiveLocale)), + this.localeConfig as Record + ); + + const dayjsParseResult = dayjs(localizedTimeString, ["LTS", "LT"]); if (dayjsParseResult.isValid()) { - let unformattedTimeString = `${dayjsParseResult.get("hour")}:${dayjsParseResult.get( - "minute" - )}`; + return { + hour: dayjsParseResult.get("hour"), + minute: dayjsParseResult.get("minute"), + second: dayjsParseResult.get("second"), + millisecond: dayjsParseResult.get("millisecond"), + }; + } + return { + hour: null, + minute: null, + second: null, + millisecond: null, + }; + } - if (this.shouldIncludeSeconds()) { - unformattedTimeString += `:${dayjsParseResult.get("seconds") || 0}`; + private getTimeStringFromParts(parts: DayjsTimeParts): string { + let timeString = ""; + if (!parts) { + return timeString; + } + if (parts.hour !== null && parts.minute !== null) { + timeString = `${formatTimePart(parts.hour)}:${formatTimePart(parts.minute)}`; + if (this.shouldIncludeSeconds() && parts.second !== null) { + timeString += `:${formatTimePart(parts.second)}`; + if (this.shouldIncludeFractionalSeconds() && parts.millisecond !== null) { + const second = (parts.millisecond * 0.001).toFixed(decimalPlaces(this.step)); + timeString += `.${second.toString().replace("0.", "")}`; + } } - - return formatTimeString(unformattedTimeString) || ""; } - return ""; + return timeString; } private popoverCloseHandler = () => { @@ -531,17 +633,20 @@ export class InputTimePicker const newValue = this.delocalizeTimeString(this.calciteInputEl.value); - this.setValue(newValue); + if (isValidTime(newValue)) { + this.setValue(newValue); - const localizedTimeString = localizeTimeString({ - value: this.value, - locale: this.effectiveLocale, - numberingSystem: this.numberingSystem, - includeSeconds: this.shouldIncludeSeconds(), - }); + const localizedTimeString = localizeTimeString({ + value: this.value, + locale: this.effectiveLocale, + numberingSystem: this.numberingSystem, + includeSeconds: this.shouldIncludeSeconds(), + fractionalSecondDigits: decimalPlaces(this.step) as FractionalSecondDigits, + }); - if (newValue && this.calciteInputEl.value !== localizedTimeString) { - this.setInputValue(localizedTimeString); + if (newValue && this.calciteInputEl.value !== localizedTimeString) { + this.setInputValue(localizedTimeString); + } } } else if (key === "ArrowDown") { this.open = true; @@ -554,22 +659,29 @@ export class InputTimePicker } }; + private getSupportedDayjsLocale(locale: string) { + const dayjsLocale = locale.toLowerCase(); + if (dayjsLocale === "no") { + return "nb"; + } + if (dayjsLocale === "pt-pt") { + return "pt"; + } + return dayjsLocale; + } + private async loadDateTimeLocaleData(): Promise { let supportedLocale = getSupportedLocale(this.effectiveLocale).toLowerCase(); - if (supportedLocale === "no") { - supportedLocale = "nb"; - } - - if (supportedLocale === "pt-pt") { - supportedLocale = "pt"; - } + supportedLocale = this.getSupportedDayjsLocale(supportedLocale); - const { default: localeConfig } = await supportedDayJsLocaleToLocaleConfigImport.get( + const { default: localeConfig } = await supportedDayjsLocaleToLocaleConfigImport.get( supportedLocale )(); - dayjs.locale(localeConfig, null, true); + this.localeConfig = localeConfig; + + dayjs.locale(this.localeConfig, null, true); dayjs.updateLocale(supportedLocale, this.getExtendedLocaleConfig(supportedLocale)); } @@ -656,6 +768,10 @@ export class InputTimePicker return this.step < 60; } + private shouldIncludeFractionalSeconds(): boolean { + return decimalPlaces(this.step) > 0; + } + private setCalcitePopoverEl = (el: HTMLCalcitePopoverElement): void => { this.popoverEl = el; }; @@ -710,6 +826,7 @@ export class InputTimePicker locale: this.effectiveLocale, numberingSystem: this.numberingSystem, includeSeconds: this.shouldIncludeSeconds(), + fractionalSecondDigits: decimalPlaces(this.step) as FractionalSecondDigits, }) ); } @@ -731,6 +848,7 @@ export class InputTimePicker includeSeconds, locale: this.effectiveLocale, numberingSystem: this.numberingSystem, + fractionalSecondDigits: decimalPlaces(this.step) as FractionalSecondDigits, }) : "" ); @@ -779,6 +897,7 @@ export class InputTimePicker locale: this.effectiveLocale, numberingSystem: this.numberingSystem, includeSeconds: this.shouldIncludeSeconds(), + fractionalSecondDigits: decimalPlaces(this.step) as FractionalSecondDigits, }) ); } diff --git a/packages/calcite-components/src/components/input-time-picker/usage/Basic.md b/packages/calcite-components/src/components/input-time-picker/usage/Basic.md index b65954ce2e3..117000f7e33 100644 --- a/packages/calcite-components/src/components/input-time-picker/usage/Basic.md +++ b/packages/calcite-components/src/components/input-time-picker/usage/Basic.md @@ -1,9 +1,3 @@ ```html - + ``` diff --git a/packages/calcite-components/src/components/input-time-picker/usage/Fractional-seconds.md b/packages/calcite-components/src/components/input-time-picker/usage/Fractional-seconds.md new file mode 100644 index 00000000000..5bf80c2ea63 --- /dev/null +++ b/packages/calcite-components/src/components/input-time-picker/usage/Fractional-seconds.md @@ -0,0 +1,3 @@ +```html + +``` diff --git a/packages/calcite-components/src/components/time-picker/assets/time-picker/t9n/messages.json b/packages/calcite-components/src/components/time-picker/assets/time-picker/t9n/messages.json index 00f28802e29..4b214d9b59e 100644 --- a/packages/calcite-components/src/components/time-picker/assets/time-picker/t9n/messages.json +++ b/packages/calcite-components/src/components/time-picker/assets/time-picker/t9n/messages.json @@ -1,4 +1,7 @@ { + "fractionalSecond": "Fractional second", + "fractionalSecondDown": "Decrease fractional second", + "fractionalSecondUp": "Increase fractional second", "hour": "Hour", "hourDown": "Decrease hour", "hourUp": "Increase hour", diff --git a/packages/calcite-components/src/components/time-picker/assets/time-picker/t9n/messages_en.json b/packages/calcite-components/src/components/time-picker/assets/time-picker/t9n/messages_en.json index 00f28802e29..4b214d9b59e 100644 --- a/packages/calcite-components/src/components/time-picker/assets/time-picker/t9n/messages_en.json +++ b/packages/calcite-components/src/components/time-picker/assets/time-picker/t9n/messages_en.json @@ -1,4 +1,7 @@ { + "fractionalSecond": "Fractional second", + "fractionalSecondDown": "Decrease fractional second", + "fractionalSecondUp": "Increase fractional second", "hour": "Hour", "hourDown": "Decrease hour", "hourUp": "Increase hour", diff --git a/packages/calcite-components/src/components/time-picker/resources.ts b/packages/calcite-components/src/components/time-picker/resources.ts index 19679137c0d..32834f0f5eb 100644 --- a/packages/calcite-components/src/components/time-picker/resources.ts +++ b/packages/calcite-components/src/components/time-picker/resources.ts @@ -2,6 +2,8 @@ export const CSS = { button: "button", buttonBottomLeft: "button--bottom-left", buttonBottomRight: "button--bottom-right", + buttonFractionalSecondDown: "button--fractionalSecond-down", + buttonFractionalSecondUp: "button--fractionalSecond-up", buttonHourDown: "button--hour-down", buttonHourUp: "button--hour-up", buttonMeridiemDown: "button--meridiem-down", @@ -14,6 +16,7 @@ export const CSS = { buttonTopRight: "button--top-right", column: "column", delimiter: "delimiter", + fractionalSecond: "fractionalSecond", hour: "hour", input: "input", meridiem: "meridiem", diff --git a/packages/calcite-components/src/components/time-picker/time-picker.e2e.ts b/packages/calcite-components/src/components/time-picker/time-picker.e2e.ts index 48f246fb47c..8b531d5ef46 100644 --- a/packages/calcite-components/src/components/time-picker/time-picker.e2e.ts +++ b/packages/calcite-components/src/components/time-picker/time-picker.e2e.ts @@ -2,6 +2,7 @@ import { newE2EPage } from "@stencil/core/testing"; import { accessible, defaults, focusable, hidden, renders, t9n } from "../../tests/commonTests"; import { formatTimePart } from "../../utils/time"; import { CSS } from "./resources"; +import { getElementXY } from "../../tests/utils"; const letterKeys = [ "a", @@ -1091,4 +1092,48 @@ describe("calcite-time-picker", () => { expect(await page.find(`calcite-time-picker >>> .${CSS.second}`)).toBeNull(); }); + + describe("fractional second support", () => { + it("upward nudge of empty fractional second sets to 0 for step=0.1", async () => { + const page = await newE2EPage(); + await page.setContent(``); + const [buttonUpLocationX, buttonUpLocationY] = await getElementXY( + page, + "calcite-time-picker", + ".button--fractionalSecond-up" + ); + await page.mouse.click(buttonUpLocationX, buttonUpLocationY); + await page.waitForChanges(); + const fractionalSecondEl = await page.find(`calcite-time-picker >>> .input.fractionalSecond`); + expect(fractionalSecondEl.innerHTML).toEqual("0"); + }); + + it("upward nudge of empty fractional second sets to 00 for step=0.01", async () => { + const page = await newE2EPage(); + await page.setContent(``); + const [buttonUpLocationX, buttonUpLocationY] = await getElementXY( + page, + "calcite-time-picker", + ".button--fractionalSecond-up" + ); + await page.mouse.click(buttonUpLocationX, buttonUpLocationY); + await page.waitForChanges(); + const fractionalSecondEl = await page.find(`calcite-time-picker >>> .input.fractionalSecond`); + expect(fractionalSecondEl.innerHTML).toEqual("00"); + }); + + it("upward nudge of empty fractional second sets to 000 for step=0.001", async () => { + const page = await newE2EPage(); + await page.setContent(``); + const [buttonUpLocationX, buttonUpLocationY] = await getElementXY( + page, + "calcite-time-picker", + ".button--fractionalSecond-up" + ); + await page.mouse.click(buttonUpLocationX, buttonUpLocationY); + await page.waitForChanges(); + const fractionalSecondEl = await page.find(`calcite-time-picker >>> .input.fractionalSecond`); + expect(fractionalSecondEl.innerHTML).toEqual("000"); + }); + }); }); diff --git a/packages/calcite-components/src/components/time-picker/time-picker.stories.ts b/packages/calcite-components/src/components/time-picker/time-picker.stories.ts new file mode 100644 index 00000000000..e688e6a9b30 --- /dev/null +++ b/packages/calcite-components/src/components/time-picker/time-picker.stories.ts @@ -0,0 +1,29 @@ +import { number, select, text } from "@storybook/addon-knobs"; +import { boolean, storyFilters } from "../../../.storybook/helpers"; +import readme from "./readme.md"; +import { html } from "../../../support/formatting"; +import { defaultMenuPlacement, menuPlacements } from "../../utils/floating-ui"; +import { locales, numberingSystems } from "../../utils/locale"; + +export default { + title: "Components/Controls/Time/Time Picker", + parameters: { + notes: readme, + }, + ...storyFilters(), +}; + +export const simple = (): string => html` + + +`; diff --git a/packages/calcite-components/src/components/time-picker/time-picker.tsx b/packages/calcite-components/src/components/time-picker/time-picker.tsx index 5b57ce14e47..e245d5197af 100644 --- a/packages/calcite-components/src/components/time-picker/time-picker.tsx +++ b/packages/calcite-components/src/components/time-picker/time-picker.tsx @@ -11,7 +11,7 @@ import { VNode, Watch, } from "@stencil/core"; -import { isActivationKey, numberKeys } from "../../utils/key"; +import { numberKeys } from "../../utils/key"; import { isValidNumber } from "../../utils/number"; import { Scale } from "../interfaces"; @@ -31,6 +31,8 @@ import { import { formatTimePart, getLocaleHourCycle, + getLocalizedDecimalSeparator, + getLocalizedTimePartSuffix, getMeridiem, getTimeParts, HourCycle, @@ -52,6 +54,7 @@ import { setComponentLoaded, setUpLoadableComponent, } from "../../utils/loadable"; +import { decimalPlaces, getDecimals } from "../../utils/math"; function capitalize(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1); @@ -82,7 +85,7 @@ export class TimePicker @Watch("step") stepChange(): void { - this.updateShowSecond(); + this.toggleSecond(); } /** @@ -96,7 +99,7 @@ export class TimePicker @Watch("value") valueWatcher(newValue: string): void { - this.setValue(newValue, false); + this.setValue(newValue); } /** @@ -128,16 +131,18 @@ export class TimePicker private activeEl: HTMLSpanElement; + private fractionalSecondEl: HTMLSpanElement; + private hourEl: HTMLSpanElement; private meridiemEl: HTMLSpanElement; + private meridiemOrder: number; + private minuteEl: HTMLSpanElement; private secondEl: HTMLSpanElement; - private meridiemOrder: number; - // -------------------------------------------------------------------------- // // State @@ -151,16 +156,22 @@ export class TimePicker this.updateLocale(); } + @State() fractionalSecond: string; + @State() hour: string; @State() hourCycle: HourCycle; + @State() localizedDecimalSeparator = "."; + @State() localizedHour: string; @State() localizedHourSuffix: string; @State() localizedMeridiem: string; + @State() localizedFractionalSecond: string; + @State() localizedMinute: string; @State() localizedMinuteSuffix: string; @@ -175,6 +186,8 @@ export class TimePicker @State() second: string; + @State() showFractionalSecond: boolean; + @State() showSecond: boolean; @State() defaultMessages: TimePickerMessages; @@ -254,6 +267,22 @@ export class TimePicker this.focusPart("minute"); event.preventDefault(); break; + case "ArrowRight": + if (this.showFractionalSecond) { + this.focusPart("fractionalSecond"); + } else if (this.hourCycle === "12") { + this.focusPart("meridiem"); + event.preventDefault(); + } + break; + } + break; + case this.fractionalSecondEl: + switch (key) { + case "ArrowLeft": + this.focusPart("second"); + event.preventDefault(); + break; case "ArrowRight": if (this.hourCycle === "12") { this.focusPart("meridiem"); @@ -265,7 +294,9 @@ export class TimePicker case this.meridiemEl: switch (key) { case "ArrowLeft": - if (this.step !== 60) { + if (this.showFractionalSecond) { + this.focusPart("fractionalSecond"); + } else if (this.step !== 60) { this.focusPart("second"); event.preventDefault(); } else { @@ -300,25 +331,15 @@ export class TimePicker // // -------------------------------------------------------------------------- - private updateShowSecond(): void { - this.showSecond = this.step < 60; - } - private async focusPart(target: TimePart): Promise { await componentFocusable(this); this[`${target || "hour"}El`]?.focus(); } - private buttonActivated(event: KeyboardEvent): boolean { - const { key } = event; - - if (key === " ") { - event.preventDefault(); - } - - return isActivationKey(key); - } + private decrementFractionalSecond = (): void => { + this.nudgeFractionalSecond("down"); + }; private decrementHour = (): void => { const newHour = !this.hour ? 0 : this.hour === "00" ? 23 : parseInt(this.hour) - 1; @@ -353,9 +374,43 @@ export class TimePicker this.activeEl = event.currentTarget as HTMLSpanElement; }; - private hourDownButtonKeyDownHandler = (event: KeyboardEvent): void => { - if (this.buttonActivated(event)) { - this.decrementHour(); + private fractionalSecondKeyDownHandler = (event: KeyboardEvent): void => { + const { key } = event; + if (numberKeys.includes(key)) { + const stepPrecision = decimalPlaces(this.step); + const fractionalSecondAsInteger = parseInt(this.fractionalSecond); + const fractionalSecondAsIntegerLength = fractionalSecondAsInteger.toString().length; + + let newFractionalSecondAsIntegerString; + + if (fractionalSecondAsIntegerLength >= stepPrecision) { + newFractionalSecondAsIntegerString = key.padStart(stepPrecision, "0"); + } else if (fractionalSecondAsIntegerLength < stepPrecision) { + newFractionalSecondAsIntegerString = `${fractionalSecondAsInteger}${key}`.padStart( + stepPrecision, + "0" + ); + } + + this.setValuePart("fractionalSecond", parseFloat(`0.${newFractionalSecondAsIntegerString}`)); + } else { + switch (key) { + case "Backspace": + case "Delete": + this.setValuePart("fractionalSecond", null); + break; + case "ArrowDown": + event.preventDefault(); + this.nudgeFractionalSecond("down"); + break; + case "ArrowUp": + event.preventDefault(); + this.nudgeFractionalSecond("up"); + break; + case " ": + event.preventDefault(); + break; + } } }; @@ -407,10 +462,8 @@ export class TimePicker } }; - private hourUpButtonKeyDownHandler = (event: KeyboardEvent): void => { - if (this.buttonActivated(event)) { - this.incrementHour(); - } + private incrementFractionalSecond = (): void => { + this.nudgeFractionalSecond("up"); }; private incrementMeridiem = (): void => { @@ -444,12 +497,6 @@ export class TimePicker this.incrementMinuteOrSecond("second"); }; - private meridiemDownButtonKeyDownHandler = (event: KeyboardEvent): void => { - if (this.buttonActivated(event)) { - this.decrementMeridiem(); - } - }; - private meridiemKeyDownHandler = (event: KeyboardEvent): void => { switch (event.key) { case "a": @@ -476,18 +523,6 @@ export class TimePicker } }; - private meridiemUpButtonKeyDownHandler = (event: KeyboardEvent): void => { - if (this.buttonActivated(event)) { - this.incrementMeridiem(); - } - }; - - private minuteDownButtonKeyDownHandler = (event: KeyboardEvent): void => { - if (this.buttonActivated(event)) { - this.decrementMinute(); - } - }; - private minuteKeyDownHandler = (event: KeyboardEvent): void => { const { key } = event; if (numberKeys.includes(key)) { @@ -524,18 +559,55 @@ export class TimePicker } }; - private minuteUpButtonKeyDownHandler = (event: KeyboardEvent): void => { - if (this.buttonActivated(event)) { - this.incrementMinute(); + private nudgeFractionalSecond = (direction: "up" | "down"): void => { + const stepDecimal = getDecimals(this.step); + const stepPrecision = decimalPlaces(this.step); + const fractionalSecondAsInteger = parseInt(this.fractionalSecond); + const fractionalSecondAsFloat = parseFloat(`0.${this.fractionalSecond}`); + let nudgedValue; + let nudgedValueRounded; + let nudgedValueRoundedDecimals; + let newFractionalSecond; + if (direction === "up") { + nudgedValue = isNaN(fractionalSecondAsInteger) ? 0 : fractionalSecondAsFloat + stepDecimal; + nudgedValueRounded = parseFloat(nudgedValue.toFixed(stepPrecision)); + nudgedValueRoundedDecimals = getDecimals(nudgedValueRounded); + newFractionalSecond = + nudgedValueRounded < 1 && decimalPlaces(nudgedValueRoundedDecimals) > 0 + ? formatTimePart(nudgedValueRoundedDecimals, stepPrecision) + : "".padStart(stepPrecision, "0"); } + if (direction === "down") { + nudgedValue = + isNaN(fractionalSecondAsInteger) || fractionalSecondAsInteger === 0 + ? 1 - stepDecimal + : fractionalSecondAsFloat - stepDecimal; + nudgedValueRounded = parseFloat(nudgedValue.toFixed(stepPrecision)); + nudgedValueRoundedDecimals = getDecimals(nudgedValueRounded); + newFractionalSecond = + nudgedValueRounded < 1 && + decimalPlaces(nudgedValueRoundedDecimals) > 0 && + Math.sign(nudgedValueRoundedDecimals) === 1 + ? formatTimePart(nudgedValueRoundedDecimals, stepPrecision) + : "".padStart(stepPrecision, "0"); + } + this.setValuePart("fractionalSecond", newFractionalSecond); }; - private secondDownButtonKeyDownHandler = (event: KeyboardEvent): void => { - if (this.buttonActivated(event)) { - this.decrementSecond(); + private sanitizeValue = (value: string): string => { + const { hour, minute, second, fractionalSecond } = parseTimeString(value); + if (fractionalSecond) { + const sanitizedFractionalSecond = this.sanitizeFractionalSecond(fractionalSecond); + return `${hour}:${minute}:${second}.${sanitizedFractionalSecond}`; } + return isValidTime(value) && value; }; + private sanitizeFractionalSecond = (fractionalSecond: string): string => + fractionalSecond && decimalPlaces(this.step) !== fractionalSecond.length + ? parseFloat(`0.${fractionalSecond}`).toFixed(decimalPlaces(this.step)).replace("0.", "") + : fractionalSecond; + private secondKeyDownHandler = (event: KeyboardEvent): void => { const { key } = event; if (numberKeys.includes(key)) { @@ -572,12 +644,6 @@ export class TimePicker } }; - private secondUpButtonKeyDownHandler = (event: KeyboardEvent): void => { - if (this.buttonActivated(event)) { - this.incrementSecond(); - } - }; - private setHourEl = (el: HTMLSpanElement) => (this.hourEl = el); private setMeridiemEl = (el: HTMLSpanElement) => (this.meridiemEl = el); @@ -586,9 +652,11 @@ export class TimePicker private setSecondEl = (el: HTMLSpanElement) => (this.secondEl = el); - private setValue = (value: string, emit = true): void => { + private setFractionalSecondEl = (el: HTMLSpanElement) => (this.fractionalSecondEl = el); + + private setValue = (value: string): void => { if (isValidTime(value)) { - const { hour, minute, second } = parseTimeString(value); + const { hour, minute, second, fractionalSecond } = parseTimeString(value); const { effectiveLocale: locale, numberingSystem } = this; const { localizedHour, @@ -596,18 +664,23 @@ export class TimePicker localizedMinute, localizedMinuteSuffix, localizedSecond, + localizedDecimalSeparator, + localizedFractionalSecond, localizedSecondSuffix, localizedMeridiem, } = localizeTimeStringToParts({ value, locale, numberingSystem }); + this.hour = hour; + this.minute = minute; + this.second = second; + this.fractionalSecond = this.sanitizeFractionalSecond(fractionalSecond); this.localizedHour = localizedHour; this.localizedHourSuffix = localizedHourSuffix; this.localizedMinute = localizedMinute; this.localizedMinuteSuffix = localizedMinuteSuffix; this.localizedSecond = localizedSecond; + this.localizedDecimalSeparator = localizedDecimalSeparator; + this.localizedFractionalSecond = localizedFractionalSecond; this.localizedSecondSuffix = localizedSecondSuffix; - this.hour = hour; - this.minute = minute; - this.second = second; if (localizedMeridiem) { this.localizedMeridiem = localizedMeridiem; this.meridiem = getMeridiem(this.hour); @@ -616,27 +689,41 @@ export class TimePicker } } else { this.hour = null; + this.fractionalSecond = null; this.localizedHour = null; - this.localizedHourSuffix = null; + this.localizedHourSuffix = getLocalizedTimePartSuffix( + "hour", + this.effectiveLocale, + this.numberingSystem + ); this.localizedMeridiem = null; this.localizedMinute = null; - this.localizedMinuteSuffix = null; + this.localizedMinuteSuffix = getLocalizedTimePartSuffix( + "minute", + this.effectiveLocale, + this.numberingSystem + ); this.localizedSecond = null; - this.localizedSecondSuffix = null; + this.localizedDecimalSeparator = getLocalizedDecimalSeparator( + this.effectiveLocale, + this.numberingSystem + ); + this.localizedFractionalSecond = null; + this.localizedSecondSuffix = getLocalizedTimePartSuffix( + "second", + this.effectiveLocale, + this.numberingSystem + ); this.meridiem = null; this.minute = null; this.second = null; this.value = null; } - if (emit) { - this.calciteInternalTimePickerChange.emit(); - } }; private setValuePart = ( - key: "hour" | "minute" | "second" | "meridiem", - value: number | string | Meridiem, - emit = true + key: "hour" | "minute" | "second" | "fractionalSecond" | "meridiem", + value: number | string | Meridiem ): void => { const { effectiveLocale: locale, numberingSystem } = this; if (key === "meridiem") { @@ -662,6 +749,20 @@ export class TimePicker numberingSystem, }); } + } else if (key === "fractionalSecond") { + const stepPrecision = decimalPlaces(this.step); + if (typeof value === "number") { + this.fractionalSecond = + value === 0 ? "".padStart(stepPrecision, "0") : formatTimePart(value, stepPrecision); + } else { + this.fractionalSecond = value; + } + this.localizedFractionalSecond = localizeTimePart({ + value: this.fractionalSecond, + part: "fractionalSecond", + locale, + numberingSystem, + }); } else { this[key] = typeof value === "number" ? formatTimePart(value) : value; this[`localized${capitalize(key)}`] = localizeTimePart({ @@ -671,15 +772,23 @@ export class TimePicker numberingSystem, }); } + let emit = false; + let newValue; if (this.hour && this.minute) { - let newValue = `${this.hour}:${this.minute}`; + newValue = `${this.hour}:${this.minute}`; if (this.showSecond) { newValue = `${newValue}:${this.second ?? "00"}`; + if (this.showFractionalSecond && this.fractionalSecond) { + newValue = `${newValue}.${this.fractionalSecond}`; + } } - this.value = newValue; } else { - this.value = null; + newValue = null; } + if (this.value !== newValue) { + emit = true; + } + this.value = newValue; this.localizedMeridiem = this.value ? localizeTimeStringToParts({ value: this.value, locale, numberingSystem }) ?.localizedMeridiem || null @@ -689,6 +798,11 @@ export class TimePicker } }; + private toggleSecond(): void { + this.showSecond = this.step < 60; + this.showFractionalSecond = decimalPlaces(this.step) > 0; + } + private getMeridiemOrder(formatParts: Intl.DateTimeFormatPart[]): number { const locale = this.effectiveLocale; const isRTLKind = locale === "ar" || locale === "he"; @@ -704,7 +818,11 @@ export class TimePicker private updateLocale() { updateMessages(this, this.effectiveLocale); this.hourCycle = getLocaleHourCycle(this.effectiveLocale, this.numberingSystem); - this.setValue(this.value, false); + this.localizedDecimalSeparator = getLocalizedDecimalSeparator( + this.effectiveLocale, + this.numberingSystem + ); + this.setValue(this.sanitizeValue(this.value)); } // -------------------------------------------------------------------------- @@ -717,7 +835,7 @@ export class TimePicker connectLocalized(this); this.updateLocale(); connectMessages(this); - this.updateShowSecond(); + this.toggleSecond(); this.meridiemOrder = this.getMeridiemOrder( getTimeParts({ value: "0:00:00", @@ -752,6 +870,7 @@ export class TimePicker const iconScale = this.scale === "s" || this.scale === "m" ? "s" : "m"; const minuteIsNumber = isValidNumber(this.minute); const secondIsNumber = isValidNumber(this.second); + const fractionalSecondIsNumber = isValidNumber(this.fractionalSecond); const showMeridiem = this.hourCycle === "12"; return (
@@ -804,7 +922,6 @@ export class TimePicker [CSS.buttonBottomLeft]: true, }} onClick={this.decrementHour} - onKeyDown={this.hourDownButtonKeyDownHandler} role="button" > @@ -819,9 +936,7 @@ export class TimePicker [CSS.buttonMinuteUp]: true, }} onClick={this.incrementMinute} - onKeyDown={this.minuteUpButtonKeyDownHandler} role="button" - tabIndex={-1} > @@ -851,7 +966,6 @@ export class TimePicker [CSS.buttonMinuteDown]: true, }} onClick={this.decrementMinute} - onKeyDown={this.minuteDownButtonKeyDownHandler} role="button" > @@ -867,7 +981,6 @@ export class TimePicker [CSS.buttonSecondUp]: true, }} onClick={this.incrementSecond} - onKeyDown={this.secondUpButtonKeyDownHandler} role="button" > @@ -898,7 +1011,54 @@ export class TimePicker [CSS.buttonSecondDown]: true, }} onClick={this.decrementSecond} - onKeyDown={this.secondDownButtonKeyDownHandler} + role="button" + > + + +
+ )} + {this.showFractionalSecond && ( + {this.localizedDecimalSeparator} + )} + {this.showFractionalSecond && ( +
+ + + + + {this.localizedFractionalSecond || "--"} + + @@ -924,7 +1084,6 @@ export class TimePicker [CSS.buttonTopRight]: true, }} onClick={this.incrementMeridiem} - onKeyDown={this.meridiemUpButtonKeyDownHandler} role="button" > @@ -956,7 +1115,6 @@ export class TimePicker [CSS.buttonBottomRight]: true, }} onClick={this.decrementMeridiem} - onKeyDown={this.meridiemDownButtonKeyDownHandler} role="button" > diff --git a/packages/calcite-components/src/demos/input-time-picker.html b/packages/calcite-components/src/demos/input-time-picker.html index 5ab370d0b29..3d222be86c6 100644 --- a/packages/calcite-components/src/demos/input-time-picker.html +++ b/packages/calcite-components/src/demos/input-time-picker.html @@ -100,6 +100,25 @@

24-Hour Locales

} else { h23.append(labelEl); } + + labelEl = document.createElement("calcite-label"); + inputTimePickerEl = document.createElement("calcite-input-time-picker"); + + inputTimePickerEl.setAttribute("lang", locale); + if (numberingSystem) { + inputTimePickerEl.setAttribute("numbering-system", numberingSystem); + } + inputTimePickerEl.setAttribute("step", 0.001); + inputTimePickerEl.setAttribute("value", "10:00:00.001"); + labelEl.append(document.createTextNode(`${name} (${locale}) (milliseconds)`)); + labelEl.append(inputTimePickerEl); + mainEl.append(labelEl); + + if (localeObject.hourCycles[0] === "h12") { + h12.append(labelEl); + } else { + h23.append(labelEl); + } }); })(); diff --git a/packages/calcite-components/src/utils/math.spec.ts b/packages/calcite-components/src/utils/math.spec.ts index 2bbdd9feb28..c6c2ed96af4 100644 --- a/packages/calcite-components/src/utils/math.spec.ts +++ b/packages/calcite-components/src/utils/math.spec.ts @@ -13,6 +13,32 @@ describe("decimalPlaces", () => { expect(decimalPlaces(123)).toBe(0); expect(decimalPlaces(123.123)).toBe(3); }); + + it("returns the amount of non-zero decimal places for a given number string", () => { + expect(decimalPlaces("0")).toBe(0); + expect(decimalPlaces("0.0")).toBe(0); + expect(decimalPlaces("0.00")).toBe(0); + expect(decimalPlaces("0.000")).toBe(0); + expect(decimalPlaces("0.1")).toBe(1); + expect(decimalPlaces("0.01")).toBe(2); + expect(decimalPlaces("0.001")).toBe(3); + expect(decimalPlaces("0.0001")).toBe(4); + }); + + it("returns the amount of decimal places for a number representation of a decimal", () => { + expect(decimalPlaces(0)).toBe(0); + expect(decimalPlaces(0.0)).toBe(0); + expect(decimalPlaces(0.1)).toBe(1); + expect(decimalPlaces(0.01)).toBe(2); + expect(decimalPlaces(0.001)).toBe(3); + expect(decimalPlaces(0.0001)).toBe(4); + expect(decimalPlaces(1)).toBe(0); + expect(decimalPlaces(1.0)).toBe(0); + expect(decimalPlaces(1.1)).toBe(1); + expect(decimalPlaces(1.01)).toBe(2); + expect(decimalPlaces(1.001)).toBe(3); + expect(decimalPlaces(1.0001)).toBe(4); + }); }); describe("remap", () => { diff --git a/packages/calcite-components/src/utils/math.ts b/packages/calcite-components/src/utils/math.ts index 8698bb5da96..3555997cf96 100644 --- a/packages/calcite-components/src/utils/math.ts +++ b/packages/calcite-components/src/utils/math.ts @@ -2,9 +2,18 @@ export const clamp = (value: number, min: number, max: number): number => Math.m const decimalNumberRegex = new RegExp(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); -export const decimalPlaces = (value: number): number => { +/** + * Returns the quantity of real decimal places for a number, which excludes trailing zeros. + * + * Adapted from {@link https://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number}. + * + * @param decimal - decimal value + * @param value + * @returns {number} the amount of decimal places in a number + */ +export const decimalPlaces = (value: number | string): number => { const match = ("" + value).match(decimalNumberRegex); - if (!match) { + if (!match || parseInt(match[1]) === 0) { return 0; } return Math.max( @@ -16,6 +25,13 @@ export const decimalPlaces = (value: number): number => { ); }; +export function getDecimals(value: number): number { + if (decimalPlaces(value) > 0 && value > 0) { + return parseFloat(`0.${value.toString().split(".")[1]}`); + } + return value; +} + export function remap(value: number, fromMin: number, fromMax: number, toMin: number, toMax: number): number { return ((value - fromMin) * (toMax - toMin)) / (fromMax - fromMin) + toMin; } diff --git a/packages/calcite-components/src/utils/time.spec.ts b/packages/calcite-components/src/utils/time.spec.ts index a7d01a4ac62..03acc940165 100644 --- a/packages/calcite-components/src/utils/time.spec.ts +++ b/packages/calcite-components/src/utils/time.spec.ts @@ -1,4 +1,198 @@ -import { toISOTimeString } from "./time"; +import { formatTimePart, isValidTime, localizeTimeStringToParts, parseTimeString, toISOTimeString } from "./time"; + +describe("formatTimePart", () => { + it("returns decimals less than 1 with leading and trailing zeros to match the provided length", () => { + expect(formatTimePart(0.3)).toEqual("3"); + expect(formatTimePart(0.3, 1)).toEqual("3"); + expect(formatTimePart(0.3, 2)).toEqual("30"); + expect(formatTimePart(0.3, 3)).toEqual("300"); + expect(formatTimePart(0.03)).toEqual("03"); + expect(formatTimePart(0.03, 2)).toEqual("03"); + expect(formatTimePart(0.03, 3)).toEqual("030"); + expect(formatTimePart(0.003)).toEqual("003"); + expect(formatTimePart(0.003, 3)).toEqual("003"); + }); + it("returns hour, minute and second values between 0 and 10 with leading zeros", () => { + expect(formatTimePart(0)).toEqual("00"); + expect(formatTimePart(1)).toEqual("01"); + expect(formatTimePart(2)).toEqual("02"); + expect(formatTimePart(3)).toEqual("03"); + expect(formatTimePart(4)).toEqual("04"); + expect(formatTimePart(5)).toEqual("05"); + expect(formatTimePart(6)).toEqual("06"); + expect(formatTimePart(7)).toEqual("07"); + expect(formatTimePart(8)).toEqual("08"); + expect(formatTimePart(9)).toEqual("09"); + }); +}); + +describe("isValidTime", () => { + it("returns true when time string contains fractional seconds", () => { + expect(isValidTime("12:30:45.0")).toBe(true); + expect(isValidTime("12:30:45.01")).toBe(true); + expect(isValidTime("12:30:45.001")).toBe(true); + expect(isValidTime("12:30:45.1")).toBe(true); + expect(isValidTime("12:30:45.12")).toBe(true); + expect(isValidTime("12:30:45.123")).toBe(true); + expect(isValidTime("12:30:45.1234")).toBe(true); + expect(isValidTime("12:30:45.12345")).toBe(true); + expect(isValidTime("12:30:45.123456")).toBe(true); + expect(isValidTime("12:30:45.1234567")).toBe(true); + expect(isValidTime("12:30:45.12345678")).toBe(true); + expect(isValidTime("12:30:45.123456789")).toBe(true); + }); +}); + +describe("localizeTimeStringToParts", () => { + it("returns localized decimal separator and fractional second value", () => { + expect(localizeTimeStringToParts({ value: "06:45:30.12123", locale: "fr" })).toEqual({ + localizedHour: "06", + localizedHourSuffix: ":", + localizedMinute: "45", + localizedMinuteSuffix: ":", + localizedSecond: "30", + localizedDecimalSeparator: ",", + localizedFractionalSecond: "12123", + localizedSecondSuffix: null, + localizedMeridiem: null, + }); + + expect(localizeTimeStringToParts({ value: "06:45:30", locale: "fr" })).toEqual({ + localizedHour: "06", + localizedHourSuffix: ":", + localizedMinute: "45", + localizedMinuteSuffix: ":", + localizedSecond: "30", + localizedDecimalSeparator: ",", + localizedFractionalSecond: null, + localizedSecondSuffix: null, + localizedMeridiem: null, + }); + + expect(localizeTimeStringToParts({ value: "06:45:30.12123", locale: "da" })).toEqual({ + localizedHour: "06", + localizedHourSuffix: ".", + localizedMinute: "45", + localizedMinuteSuffix: ".", + localizedSecond: "30", + localizedDecimalSeparator: ",", + localizedFractionalSecond: "12123", + localizedSecondSuffix: null, + localizedMeridiem: null, + }); + }); + + it("returns fractional second value with padded zeros when necessary", () => { + expect(localizeTimeStringToParts({ value: "06:45:30.04", locale: "en" })).toEqual({ + localizedHour: "06", + localizedHourSuffix: ":", + localizedMinute: "45", + localizedMinuteSuffix: ":", + localizedSecond: "30", + localizedDecimalSeparator: ".", + localizedFractionalSecond: "04", + localizedSecondSuffix: null, + localizedMeridiem: "AM", + }); + expect(localizeTimeStringToParts({ value: "06:45:30.003", locale: "en" })).toEqual({ + localizedHour: "06", + localizedHourSuffix: ":", + localizedMinute: "45", + localizedMinuteSuffix: ":", + localizedSecond: "30", + localizedDecimalSeparator: ".", + localizedFractionalSecond: "003", + localizedSecondSuffix: null, + localizedMeridiem: "AM", + }); + expect(localizeTimeStringToParts({ value: "06:45:30.007", locale: "ar", numberingSystem: "arab" })).toEqual({ + localizedHour: "٠٦", + localizedHourSuffix: ":", + localizedMinute: "٤٥", + localizedMinuteSuffix: ":", + localizedSecond: "٣٠", + localizedDecimalSeparator: "٫", + localizedFractionalSecond: "٠٠٧", + localizedSecondSuffix: null, + localizedMeridiem: "ص", + }); + }); +}); + +describe("parseTimeString", () => { + it("returns literal hour, minute, second and fractional second values from given string", () => { + expect(parseTimeString("12:30:45.0")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "0", + }); + expect(parseTimeString("12:30:45.01")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "01", + }); + expect(parseTimeString("12:30:45.001")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "001", + }); + expect(parseTimeString("12:30:45.0001")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "0001", + }); + expect(parseTimeString("12:30:45.0049")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "0049", + }); + expect(parseTimeString("12:30:45.1")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "1", + }); + expect(parseTimeString("12:30:45.12")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "12", + }); + expect(parseTimeString("12:30:45.123")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "123", + }); + expect(parseTimeString("12:30:45.1234")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "1234", + }); + expect(parseTimeString("12:30:45.12345")).toEqual({ + hour: "12", + minute: "30", + second: "45", + fractionalSecond: "12345", + }); + expect(parseTimeString("12:30:45.12345.34")).toEqual({ + hour: null, + minute: null, + second: null, + fractionalSecond: null, + }); + }); + + it("returns null fractionalSecond when second is a whole number", () => { + expect(parseTimeString("12:30:45")).toEqual({ fractionalSecond: null, hour: "12", minute: "30", second: "45" }); + }); +}); describe("toISOTimeString", () => { it("returns hh:mm value when includeSeconds is false", () => { diff --git a/packages/calcite-components/src/utils/time.ts b/packages/calcite-components/src/utils/time.ts index b87207c9f55..adf4c0416c3 100644 --- a/packages/calcite-components/src/utils/time.ts +++ b/packages/calcite-components/src/utils/time.ts @@ -1,5 +1,9 @@ -import { getDateTimeFormat, getSupportedNumberingSystem, NumberingSystem } from "./locale"; +import { getDateTimeFormat, getSupportedNumberingSystem, NumberingSystem, numberStringFormatter } from "./locale"; +import { decimalPlaces } from "./math"; import { isValidNumber } from "./number"; + +export type FractionalSecondDigits = 1 | 2 | 3; + export type HourCycle = "12" | "24"; export interface LocalizedTime { @@ -8,6 +12,8 @@ export interface LocalizedTime { localizedMinute: string; localizedMinuteSuffix: string; localizedSecond: string; + localizedDecimalSeparator: string; + localizedFractionalSecond: string; localizedSecondSuffix: string; localizedMeridiem: string; } @@ -17,19 +23,30 @@ export type Meridiem = "AM" | "PM"; export type MinuteOrSecond = "minute" | "second"; export interface Time { + fractionalSecond: string; hour: string; minute: string; second: string; } -export type TimePart = "hour" | "hourSuffix" | "minute" | "minuteSuffix" | "second" | "secondSuffix" | "meridiem"; +export type TimePart = + | "hour" + | "hourSuffix" + | "minute" + | "minuteSuffix" + | "second" + | "decimalSeparator" + | "fractionalSecond" + | "secondSuffix" + | "meridiem"; export const maxTenthForMinuteAndSecond = 5; function createLocaleDateTimeFormatter( locale: string, numberingSystem: NumberingSystem, - includeSeconds = true + includeSeconds = true, + fractionalSecondDigits?: FractionalSecondDigits ): Intl.DateTimeFormat { const options: Intl.DateTimeFormatOptions = { hour: "2-digit", @@ -39,28 +56,55 @@ function createLocaleDateTimeFormatter( }; if (includeSeconds) { options.second = "2-digit"; + if (fractionalSecondDigits) { + options.fractionalSecondDigits = fractionalSecondDigits; + } } return getDateTimeFormat(locale, options); } -export function formatTimePart(number: number): string { +export function formatTimePart(number: number, minLength?: number): string { + if (number === null || number === undefined) { + return; + } const numberAsString = number.toString(); - return number >= 0 && number <= 9 ? numberAsString.padStart(2, "0") : numberAsString; + const numberDecimalPlaces = decimalPlaces(number); + if (number < 1 && numberDecimalPlaces > 0 && numberDecimalPlaces < 4) { + const fractionalDigits = numberAsString.replace("0.", ""); + if (!minLength || fractionalDigits.length === minLength) { + return fractionalDigits; + } + if (fractionalDigits.length < minLength) { + return fractionalDigits.padEnd(minLength, "0"); + } + return fractionalDigits; + } + if (number >= 0 && number < 10) { + return numberAsString.padStart(2, "0"); + } + if (number >= 10) { + return numberAsString; + } } export function formatTimeString(value: string): string { if (!isValidTime(value)) { return null; } - const [hourString, minuteString, secondString] = value.split(":"); - const hour = formatTimePart(parseInt(hourString)); - const minute = formatTimePart(parseInt(minuteString)); - if (secondString) { - const second = formatTimePart(parseInt(secondString)); - return `${hour}:${minute}:${second}`; + const { hour, minute, second, fractionalSecond } = parseTimeString(value); + let formattedValue = `${formatTimePart(parseInt(hour))}:${formatTimePart(parseInt(minute))}`; + if (second) { + formattedValue += `:${formatTimePart(parseInt(second))}`; + if (fractionalSecond) { + formattedValue += `.${fractionalSecond}`; + } } - return `${hour}:${minute}`; + return formattedValue; +} + +function fractionalSecondPartToMilliseconds(fractionalSecondPart: string): number { + return parseInt((parseFloat(`0.${fractionalSecondPart}`) / 0.001).toFixed(3)); } export function getLocaleHourCycle(locale: string, numberingSystem: NumberingSystem): HourCycle { @@ -69,6 +113,24 @@ export function getLocaleHourCycle(locale: string, numberingSystem: NumberingSys return getLocalizedTimePart("meridiem", parts) ? "12" : "24"; } +export function getLocalizedDecimalSeparator(locale: string, numberingSystem: NumberingSystem): string { + numberStringFormatter.numberFormatOptions = { + locale, + numberingSystem, + }; + return numberStringFormatter.localize("1.1").split("")[1]; +} + +export function getLocalizedTimePartSuffix( + part: "hour" | "minute" | "second", + locale: string, + numberingSystem: NumberingSystem = "latn" +): string { + const formatter = createLocaleDateTimeFormatter(locale, numberingSystem); + const parts = formatter.formatToParts(new Date(Date.UTC(0, 0, 0, 0, 0, 0))); + return getLocalizedTimePart(`${part}Suffix` as TimePart, parts); +} + function getLocalizedTimePart(part: TimePart, parts: Intl.DateTimeFormatPart[]): string { if (!part || !parts) { return null; @@ -145,6 +207,29 @@ interface LocalizeTimePartParameters { } export function localizeTimePart({ value, part, locale, numberingSystem }: LocalizeTimePartParameters): string { + if (part === "fractionalSecond") { + const localizedDecimalSeparator = getLocalizedDecimalSeparator(locale, numberingSystem); + let localizedFractionalSecond = null; + if (value) { + numberStringFormatter.numberFormatOptions = { + locale, + numberingSystem, + }; + const localizedZero = numberStringFormatter.localize("0"); + if (parseInt(value) === 0) { + localizedFractionalSecond = "".padStart(value.length, localizedZero); + } else { + localizedFractionalSecond = numberStringFormatter + .localize(`0.${value}`) + .replace(`${localizedZero}${localizedDecimalSeparator}`, ""); + if (localizedFractionalSecond.length < value.length) { + localizedFractionalSecond = localizedFractionalSecond.padEnd(value.length, localizedZero); + } + } + } + return localizedFractionalSecond; + } + if (!isValidTimePart(value, part)) { return; } @@ -170,6 +255,7 @@ export function localizeTimePart({ value, part, locale, numberingSystem }: Local interface LocalizeTimeStringParameters { value: string; includeSeconds?: boolean; + fractionalSecondDigits?: FractionalSecondDigits; locale: string; numberingSystem: NumberingSystem; } @@ -179,32 +265,43 @@ export function localizeTimeString({ locale, numberingSystem, includeSeconds = true, + fractionalSecondDigits, }: LocalizeTimeStringParameters): string { if (!isValidTime(value)) { return null; } - const { hour, minute, second = "0" } = parseTimeString(value); - const dateFromTimeString = new Date(Date.UTC(0, 0, 0, parseInt(hour), parseInt(minute), parseInt(second))); - const formatter = createLocaleDateTimeFormatter(locale, numberingSystem, includeSeconds); - return formatter?.format(dateFromTimeString) || null; + const { hour, minute, second = "0", fractionalSecond } = parseTimeString(value); + + const dateFromTimeString = new Date( + Date.UTC( + 0, + 0, + 0, + parseInt(hour), + parseInt(minute), + parseInt(second), + fractionalSecond && fractionalSecondPartToMilliseconds(fractionalSecond) + ) + ); + const formatter = createLocaleDateTimeFormatter(locale, numberingSystem, includeSeconds, fractionalSecondDigits); + return formatter.format(dateFromTimeString) || null; } interface LocalizeTimeStringToPartsParameters { value: string; locale: string; - numberingSystem: NumberingSystem; + numberingSystem?: NumberingSystem; } export function localizeTimeStringToParts({ value, locale, - numberingSystem, + numberingSystem = "latn", }: LocalizeTimeStringToPartsParameters): LocalizedTime { if (!isValidTime(value)) { return null; } - - const { hour, minute, second = "0" } = parseTimeString(value); + const { hour, minute, second = "0", fractionalSecond } = parseTimeString(value); const dateFromTimeString = new Date(Date.UTC(0, 0, 0, parseInt(hour), parseInt(minute), parseInt(second))); if (dateFromTimeString) { const formatter = createLocaleDateTimeFormatter(locale, numberingSystem); @@ -215,6 +312,13 @@ export function localizeTimeStringToParts({ localizedMinute: getLocalizedTimePart("minute", parts), localizedMinuteSuffix: getLocalizedTimePart("minuteSuffix", parts), localizedSecond: getLocalizedTimePart("second", parts), + localizedDecimalSeparator: getLocalizedDecimalSeparator(locale, numberingSystem), + localizedFractionalSecond: localizeTimePart({ + value: fractionalSecond, + part: "fractionalSecond", + locale, + numberingSystem, + }), localizedSecondSuffix: getLocalizedTimePart("secondSuffix", parts), localizedMeridiem: getLocalizedTimePart("meridiem", parts), }; @@ -243,14 +347,21 @@ export function getTimeParts({ value, locale, numberingSystem }: GetTimePartsPar export function parseTimeString(value: string): Time { if (isValidTime(value)) { - const [hour, minute, second] = value.split(":"); + const [hour, minute, secondDecimal] = value.split(":"); + let second = secondDecimal; + let fractionalSecond = null; + if (secondDecimal?.includes(".")) { + [second, fractionalSecond] = secondDecimal.split("."); + } return { + fractionalSecond, hour, minute, second, }; } return { + fractionalSecond: null, hour: null, minute: null, second: null, @@ -261,12 +372,15 @@ export function toISOTimeString(value: string, includeSeconds = true): string { if (!isValidTime(value)) { return ""; } - const { hour, minute, second } = parseTimeString(value); + const { hour, minute, second, fractionalSecond } = parseTimeString(value); let isoTimeString = `${formatTimePart(parseInt(hour))}:${formatTimePart(parseInt(minute))}`; if (includeSeconds) { isoTimeString += `:${formatTimePart(parseInt((includeSeconds && second) || "0"))}`; + if (fractionalSecond) { + isoTimeString += `.${fractionalSecond}`; + } } return isoTimeString; From 237a2c53560fd662ea78bd1d31911db5c2ae59a8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 31 Aug 2023 02:16:43 +0000 Subject: [PATCH 05/35] chore: release next --- package-lock.json | 8 ++++---- packages/calcite-components-react/CHANGELOG.md | 4 ++++ packages/calcite-components-react/package.json | 4 ++-- packages/calcite-components/CHANGELOG.md | 6 ++++++ packages/calcite-components/package.json | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index b58b8d665fa..3d8a835547a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40443,7 +40443,7 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "1.7.0-next.13", + "version": "1.7.0-next.14", "license": "SEE LICENSE.md", "dependencies": { "@floating-ui/dom": "1.5.1", @@ -40470,10 +40470,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "1.7.0-next.13", + "version": "1.7.0-next.14", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^1.7.0-next.13" + "@esri/calcite-components": "^1.7.0-next.14" }, "peerDependencies": { "react": ">=16.7", @@ -42704,7 +42704,7 @@ "@esri/calcite-components-react": { "version": "file:packages/calcite-components-react", "requires": { - "@esri/calcite-components": "^1.7.0-next.13" + "@esri/calcite-components": "^1.7.0-next.14" } }, "@esri/calcite-design-tokens": { diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index ebadf944994..54eb253daa7 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.14](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.13...@esri/calcite-components-react@1.7.0-next.14) (2023-08-31) + +**Note:** Version bump only for package @esri/calcite-components-react + ## [1.7.0-next.13](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.12...@esri/calcite-components-react@1.7.0-next.13) (2023-08-30) **Note:** Version bump only for package @esri/calcite-components-react diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index e4d43b98140..7e22e99d23f 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,7 +1,7 @@ { "name": "@esri/calcite-components-react", "sideEffects": false, - "version": "1.7.0-next.13", + "version": "1.7.0-next.14", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", @@ -20,7 +20,7 @@ "dist/" ], "dependencies": { - "@esri/calcite-components": "^1.7.0-next.13" + "@esri/calcite-components": "^1.7.0-next.14" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index 20cfa54186e..43a7f50810c 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.14](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.13...@esri/calcite-components@1.7.0-next.14) (2023-08-31) + +### Features + +- **input-time-picker:** support fractional seconds ([#7532](https://github.com/Esri/calcite-design-system/issues/7532)) ([c2bf34b](https://github.com/Esri/calcite-design-system/commit/c2bf34b69c6c199c4709774b2d80b71d3b2f9b9e)), closes [#6591](https://github.com/Esri/calcite-design-system/issues/6591) + ## [1.7.0-next.13](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.12...@esri/calcite-components@1.7.0-next.13) (2023-08-30) ### Features diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index 2a1d537e911..408c44900ae 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "1.7.0-next.13", + "version": "1.7.0-next.14", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", From 0ca35e92afe694841ff677b58be99bbe950d52aa Mon Sep 17 00:00:00 2001 From: Anveshreddy mekala Date: Wed, 30 Aug 2023 23:24:24 -0500 Subject: [PATCH 06/35] feat(input-date-picker): normalize year to current century (#7622) **Related Issue:** #7588 ## Summary Normalizes year to current century. Ex: If the user types `01/01/20` the `value` will be corrected to `01/01/2020` --- .../input-date-picker.e2e.ts | 70 +++++++++++++++++++ .../input-date-picker.stories.ts | 13 ++++ .../input-date-picker/input-date-picker.tsx | 54 +++++++++++++- .../components/input-date-picker/util.spec.ts | 19 +++++ .../src/components/input-date-picker/utils.ts | 27 +++++++ .../calcite-components/src/utils/date.spec.ts | 8 +++ packages/calcite-components/src/utils/date.ts | 11 +++ 7 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 packages/calcite-components/src/components/input-date-picker/util.spec.ts create mode 100644 packages/calcite-components/src/components/input-date-picker/utils.ts diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts b/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts index ba05c44a2e1..3cb4e147b1c 100644 --- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts +++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts @@ -849,4 +849,74 @@ describe("calcite-input-date-picker", () => { expect(await getActiveMonth(page)).toBe("October"); }); + + describe("normalize year", () => { + it("should normalize year to current century when user types the value", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + + const element = await page.find("calcite-input-date-picker"); + const changeEvent = await page.spyOnEvent("calciteInputDatePickerChange"); + + await element.click(); + await page.waitForChanges(); + await page.keyboard.type("3/7/20"); + await page.keyboard.press("Enter"); + await page.waitForChanges(); + + expect(await element.getProperty("value")).toBe("2020-03-07"); + expect(await element.getProperty("valueAsDate")).toBeDefined(); + expect(changeEvent).toHaveReceivedEventTimes(1); + }); + + it("should normalize year to current century when value is parsed as attribute", async () => { + const page = await newE2EPage(); + await page.setContent( + html`` + ); + + const element = await page.find("calcite-input-date-picker"); + const changeEvent = await page.spyOnEvent("calciteInputDatePickerChange"); + + expect(await element.getProperty("value")).toBe("2020-01-01"); + expect(await element.getProperty("valueAsDate")).toBeDefined(); + expect(changeEvent).toHaveReceivedEventTimes(0); + }); + + it("should normalize year to current century when user types the value in range", async () => { + const page = await newE2EPage(); + await page.setContent(""); + const element = await page.find("calcite-input-date-picker"); + const changeEvent = await page.spyOnEvent("calciteInputDatePickerChange"); + + await element.click(); + await page.waitForChanges(); + await page.keyboard.type("1/1/20"); + await page.keyboard.press("Enter"); + await page.waitForChanges(); + + expect(await element.getProperty("value")).toEqual(["2020-01-01", ""]); + expect(changeEvent).toHaveReceivedEventTimes(1); + + await page.keyboard.type("2/2/20"); + await page.keyboard.press("Enter"); + await page.waitForChanges(); + + expect(await element.getProperty("value")).toEqual(["2020-01-01", "2020-02-02"]); + expect(changeEvent).toHaveReceivedEventTimes(2); + }); + + it("should normalize year to current century when value is changed programmatically in range", async () => { + const page = await newE2EPage(); + await page.setContent(""); + const element = await page.find("calcite-input-date-picker"); + const changeEvent = await page.spyOnEvent("calciteInputDatePickerChange"); + + element.setProperty("value", ["00-03-07", "00-03-08"]); + await page.waitForChanges(); + + expect(await element.getProperty("value")).toEqual(["2000-03-07", "2000-03-08"]); + expect(changeEvent).toHaveReceivedEventTimes(0); + }); + }); }); diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.stories.ts b/packages/calcite-components/src/components/input-date-picker/input-date-picker.stories.ts index 3383705be8c..e8f71139845 100644 --- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.stories.ts +++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.stories.ts @@ -96,3 +96,16 @@ export const darkModeRTL_TestOnly = (): string => html`
`; darkModeRTL_TestOnly.parameters = { modes: modesDarkDefault }; + +export const normalizeYearWithGermanLocale_TestOnly = (): string => html` +
+ +
+ +`; diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx b/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx index c34ff9e4b08..ba4a62b2575 100644 --- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx +++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx @@ -17,6 +17,7 @@ import { dateFromISO, dateFromLocalizedString, dateFromRange, + datePartsFromISO, datePartsFromLocalizedString, dateToISO, inRange, @@ -77,6 +78,7 @@ import { } from "../../utils/focusTrapComponent"; import { FocusTrap } from "focus-trap"; import { guid } from "../../utils/guid"; +import { normalizeToCurrentCentury, isTwoDigitYear } from "./utils"; @Component({ tag: "calcite-input-date-picker", @@ -155,8 +157,16 @@ export class InputDatePicker let newValueAsDate: Date | Date[]; if (Array.isArray(newValue)) { + if (isTwoDigitYear(newValue[0]) || isTwoDigitYear(newValue[1])) { + this.value = newValue.map((val) => this.getNormalizedDate(val)); + return; + } newValueAsDate = getValueAsDateRange(newValue); } else if (newValue) { + if (isTwoDigitYear(newValue)) { + this.value = this.getNormalizedDate(newValue); + return; + } newValueAsDate = dateFromISO(newValue); } else { newValueAsDate = undefined; @@ -273,6 +283,11 @@ export class InputDatePicker */ @Prop({ reflect: true }) name: string; + /** + * Normalizes year to current century. + */ + @Prop({ reflect: true }) normalizeYear = false; + /** * Specifies the Unicode numeral system used by the component for localization. This property cannot be dynamically changed. * @@ -435,9 +450,17 @@ export class InputDatePicker const { open } = this; open && this.openHandler(open); if (Array.isArray(this.value)) { + if (isTwoDigitYear(this.value[0]) || isTwoDigitYear(this.value[1])) { + this.value = this.value.map((val) => this.getNormalizedDate(val)); + return; + } this.valueAsDate = getValueAsDateRange(this.value); } else if (this.value) { try { + if (isTwoDigitYear(this.value)) { + this.value = this.getNormalizedDate(this.value); + return; + } this.valueAsDate = dateFromISO(this.value); } catch (error) { this.warnAboutInvalidValue(this.value); @@ -1027,9 +1050,16 @@ export class InputDatePicker const valueIsArray = Array.isArray(valueAsDate); const newStartDate = valueIsArray ? valueAsDate[0] : null; - const newStartDateISO = valueIsArray ? dateToISO(newStartDate) : ""; + let newStartDateISO = valueIsArray ? dateToISO(newStartDate) : ""; + if (newStartDateISO && isTwoDigitYear(newStartDateISO)) { + newStartDateISO = this.getNormalizedDate(newStartDateISO); + } + const newEndDate = valueIsArray ? valueAsDate[1] : null; - const newEndDateISO = valueIsArray ? dateToISO(newEndDate) : ""; + let newEndDateISO = valueIsArray ? dateToISO(newEndDate) : ""; + if (newEndDateISO && isTwoDigitYear(newEndDateISO)) { + newEndDateISO = this.getNormalizedDate(newEndDateISO); + } const newValue = newStartDateISO || newEndDateISO ? [newStartDateISO, newEndDateISO] : ""; @@ -1061,7 +1091,11 @@ export class InputDatePicker } const oldValue = this.value; - const newValue = dateToISO(value as Date); + let newValue = dateToISO(value as Date); + + if (isTwoDigitYear(newValue)) { + newValue = this.getNormalizedDate(newValue); + } if (newValue === oldValue) { return; @@ -1110,4 +1144,18 @@ export class InputDatePicker ) .join("") : ""; + + private getNormalizedDate(value: string): string { + if (!value) { + return ""; + } + + if (!this.normalizeYear) { + return value; + } + + const { day, month, year } = datePartsFromISO(value); + const normalizedYear = normalizeToCurrentCentury(Number(year)); + return `${normalizedYear}-${month}-${day}`; + } } diff --git a/packages/calcite-components/src/components/input-date-picker/util.spec.ts b/packages/calcite-components/src/components/input-date-picker/util.spec.ts new file mode 100644 index 00000000000..f46cca94134 --- /dev/null +++ b/packages/calcite-components/src/components/input-date-picker/util.spec.ts @@ -0,0 +1,19 @@ +import { isTwoDigitYear, normalizeToCurrentCentury } from "./utils"; + +describe("utils", () => { + describe("isTwoDigitYear", () => { + it("returns whether a given ISO string date has two digit year", () => { + expect(isTwoDigitYear("2023-08-01")).toBe(false); + expect(isTwoDigitYear("00-08-01")).toBe(true); + expect(isTwoDigitYear("")).toBe(false); + }); + }); + + describe("normalizedYear", () => { + it("return normalized date for a given ISO string date with two digit year", () => { + expect(normalizeToCurrentCentury(20)).toEqual(2020); + expect(normalizeToCurrentCentury(0)).toBe(2000); + expect(normalizeToCurrentCentury(1)).toBe(2001); + }); + }); +}); diff --git a/packages/calcite-components/src/components/input-date-picker/utils.ts b/packages/calcite-components/src/components/input-date-picker/utils.ts new file mode 100644 index 00000000000..1a7c39f273f --- /dev/null +++ b/packages/calcite-components/src/components/input-date-picker/utils.ts @@ -0,0 +1,27 @@ +import { datePartsFromISO } from "../../utils/date"; + +/** + * Specifies if an ISO string date (YYYY-MM-DD) has two digit year. + * + * @param {string} value + * @returns {boolean} + */ +export function isTwoDigitYear(value: string): boolean { + if (!value) { + return false; + } + const { year } = datePartsFromISO(value); + return Number(year) < 100; +} + +/** + * Returns a normalized year to current century from a given two digit year number. + * + * @param {number} twoDigitYear + * @returns {string} + */ +export function normalizeToCurrentCentury(twoDigitYear: number): number { + const currentYear = new Date().getFullYear(); + const normalizedYear = Math.floor(currentYear / 100) * 100 + twoDigitYear; + return normalizedYear; +} diff --git a/packages/calcite-components/src/utils/date.spec.ts b/packages/calcite-components/src/utils/date.spec.ts index 9d5200c0050..dff6d9c2545 100644 --- a/packages/calcite-components/src/utils/date.spec.ts +++ b/packages/calcite-components/src/utils/date.spec.ts @@ -2,6 +2,7 @@ import { DateLocaleData } from "../components/date-picker/utils"; import { dateFromISO, dateFromRange, + datePartsFromISO, dateToISO, formatCalendarYear, getOrder, @@ -250,3 +251,10 @@ describe("parseCalendarYear", () => { expect(parseCalendarYear(2566, { "default-calendar": "buddhist" } as DateLocaleData)).toBe(2023); }); }); + +describe("datePartsFromISO", () => { + it("returns date, year, month from parsed ISO string date", () => { + expect(datePartsFromISO("2023-08-01")).toEqual({ day: "01", month: "08", year: "2023" }); + expect(datePartsFromISO("00-08-01")).toEqual({ day: "01", month: "08", year: "00" }); + }); +}); diff --git a/packages/calcite-components/src/utils/date.ts b/packages/calcite-components/src/utils/date.ts index 1a38876a016..cbb9ee1b3fa 100644 --- a/packages/calcite-components/src/utils/date.ts +++ b/packages/calcite-components/src/utils/date.ts @@ -156,6 +156,17 @@ export function dateToISO(date?: Date): string { return ""; } +/** + * Retrieve day, month, and year strings from a ISO string (YYYY-mm-dd) + * + * @param string + * @param isoDate + */ +export function datePartsFromISO(isoDate: string): { day: string; month: string; year: string } { + const dateParts = isoDate.split("-"); + return { day: dateParts[2], month: dateParts[1], year: dateParts[0] }; +} + /** * Check if two dates are the same day, month, year * From b7f99f94bf1fbbd259c4a44da742f5f35a8b1ec7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 31 Aug 2023 04:44:22 +0000 Subject: [PATCH 07/35] chore: release next --- package-lock.json | 8 ++++---- packages/calcite-components-react/CHANGELOG.md | 4 ++++ packages/calcite-components-react/package.json | 4 ++-- packages/calcite-components/CHANGELOG.md | 6 ++++++ packages/calcite-components/package.json | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d8a835547a..28e541071a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40443,7 +40443,7 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "1.7.0-next.14", + "version": "1.7.0-next.15", "license": "SEE LICENSE.md", "dependencies": { "@floating-ui/dom": "1.5.1", @@ -40470,10 +40470,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "1.7.0-next.14", + "version": "1.7.0-next.15", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^1.7.0-next.14" + "@esri/calcite-components": "^1.7.0-next.15" }, "peerDependencies": { "react": ">=16.7", @@ -42704,7 +42704,7 @@ "@esri/calcite-components-react": { "version": "file:packages/calcite-components-react", "requires": { - "@esri/calcite-components": "^1.7.0-next.14" + "@esri/calcite-components": "^1.7.0-next.15" } }, "@esri/calcite-design-tokens": { diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index 54eb253daa7..7896d751dcf 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.15](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.14...@esri/calcite-components-react@1.7.0-next.15) (2023-08-31) + +**Note:** Version bump only for package @esri/calcite-components-react + ## [1.7.0-next.14](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.13...@esri/calcite-components-react@1.7.0-next.14) (2023-08-31) **Note:** Version bump only for package @esri/calcite-components-react diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index 7e22e99d23f..d139712d377 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,7 +1,7 @@ { "name": "@esri/calcite-components-react", "sideEffects": false, - "version": "1.7.0-next.14", + "version": "1.7.0-next.15", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", @@ -20,7 +20,7 @@ "dist/" ], "dependencies": { - "@esri/calcite-components": "^1.7.0-next.14" + "@esri/calcite-components": "^1.7.0-next.15" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index 43a7f50810c..f7a1ae9e6a1 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.15](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.14...@esri/calcite-components@1.7.0-next.15) (2023-08-31) + +### Features + +- **input-date-picker:** normalize year to current century ([#7622](https://github.com/Esri/calcite-design-system/issues/7622)) ([0ca35e9](https://github.com/Esri/calcite-design-system/commit/0ca35e92afe694841ff677b58be99bbe950d52aa)), closes [#7588](https://github.com/Esri/calcite-design-system/issues/7588) + ## [1.7.0-next.14](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.13...@esri/calcite-components@1.7.0-next.14) (2023-08-31) ### Features diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index 408c44900ae..1885da75222 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "1.7.0-next.14", + "version": "1.7.0-next.15", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", From 15a361438e49049c6d51d2b4fa5da7189db4210e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 23:16:13 -0700 Subject: [PATCH 08/35] build(deps): update dependency @rollup/plugin-node-resolve to v15.2.1 (#7620) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@rollup/plugin-node-resolve](https://togithub.com/rollup/plugins/tree/master/packages/node-resolve/#readme) ([source](https://togithub.com/rollup/plugins)) | [`15.2.0` -> `15.2.1`](https://renovatebot.com/diffs/npm/@rollup%2fplugin-node-resolve/15.2.0/15.2.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@rollup%2fplugin-node-resolve/15.2.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@rollup%2fplugin-node-resolve/15.2.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@rollup%2fplugin-node-resolve/15.2.0/15.2.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@rollup%2fplugin-node-resolve/15.2.0/15.2.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
rollup/plugins (@​rollup/plugin-node-resolve) ### [`v15.2.1`](https://togithub.com/rollup/plugins/blob/HEAD/packages/node-resolve/CHANGELOG.md#v1521) [Compare Source](https://togithub.com/rollup/plugins/compare/7d395b8828db2b208c561a5bda87997c00ecf2b6...767a8f5b49a3c871d681407a7b237475a7c6a97d) *2023-08-22* ##### Bugfixes - fix: Implement package exports / imports resolution algorithm according to Node documentation [#​1549](https://togithub.com/rollup/plugins/pull/1549)
--- ### Configuration 📅 **Schedule**: Branch creation - "before 5am on tuesday and thursday" in timezone America/Los_Angeles, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/Esri/calcite-design-system). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Ben Elan --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 28e541071a7..840227072c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@cspell/eslint-plugin": "7.0.1", "@esri/calcite-base": "1.2.0", "@esri/calcite-colors": "6.1.0", - "@rollup/plugin-node-resolve": "15.2.0", + "@rollup/plugin-node-resolve": "15.2.1", "@rollup/plugin-typescript": "11.1.2", "@storybook/addon-a11y": "6.5.16", "@storybook/addon-docs": "6.5.16", @@ -6347,9 +6347,9 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.0.tgz", - "integrity": "sha512-mKur03xNGT8O9ODO6FtT43ITGqHWZbKPdVJHZb+iV9QYcdlhUUB0wgknvA4KCUmC5oHJF6O2W1EgmyOQyVUI4Q==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz", + "integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -45177,9 +45177,9 @@ } }, "@rollup/plugin-node-resolve": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.0.tgz", - "integrity": "sha512-mKur03xNGT8O9ODO6FtT43ITGqHWZbKPdVJHZb+iV9QYcdlhUUB0wgknvA4KCUmC5oHJF6O2W1EgmyOQyVUI4Q==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz", + "integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==", "dev": true, "requires": { "@rollup/pluginutils": "^5.0.1", diff --git a/package.json b/package.json index 714977cfa65..e86b81216c4 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@cspell/eslint-plugin": "7.0.1", "@esri/calcite-base": "1.2.0", "@esri/calcite-colors": "6.1.0", - "@rollup/plugin-node-resolve": "15.2.0", + "@rollup/plugin-node-resolve": "15.2.1", "@rollup/plugin-typescript": "11.1.2", "@storybook/addon-a11y": "6.5.16", "@storybook/addon-docs": "6.5.16", From 79c3d56eb6e7317981f368006d426a8aa2f6cf25 Mon Sep 17 00:00:00 2001 From: Kitty Hurley Date: Thu, 31 Aug 2023 10:57:27 -0500 Subject: [PATCH 09/35] chore(deps): update calcite-ui-icons to 3.24.1 (#7637) **Related Issue:** n/a ## Summary Updates `calcite-ui-icons` to `3.24.1`, released yesterday, 8/30, for the upcoming CC `1.7.0` release. --- package-lock.json | 16 ++++++++-------- packages/calcite-components/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 840227072c4..9cb03ab3fc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3092,9 +3092,9 @@ "dev": true }, "node_modules/@esri/calcite-ui-icons": { - "version": "3.23.9", - "resolved": "https://registry.npmjs.org/@esri/calcite-ui-icons/-/calcite-ui-icons-3.23.9.tgz", - "integrity": "sha512-9rbdZEV/v2gq7p11KtLxMamamwG9iH2z/8m3y2GFn7HzF9CqOMYP6fwBu4XBjk3uW05+5T6Vksz7Xn9obq9wrg==", + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/@esri/calcite-ui-icons/-/calcite-ui-icons-3.24.1.tgz", + "integrity": "sha512-mPy4i2ivT8d4ytmtK3gOVfo+ldYUIeRRKPgBTExGf5pZBIS6xIu+g5OYqAhTquREivnQBoMMzo2FgDZhKxnuSg==", "dev": true, "bin": { "spriter": "bin/spriter.js" @@ -40459,7 +40459,7 @@ }, "devDependencies": { "@esri/calcite-design-tokens": "1.0.0", - "@esri/calcite-ui-icons": "3.23.9", + "@esri/calcite-ui-icons": "3.24.1", "@esri/eslint-plugin-calcite-components": "^0.2.3-next.4", "@stencil-community/eslint-plugin": "0.5.0", "@stencil/postcss": "2.1.0", @@ -42682,7 +42682,7 @@ "version": "file:packages/calcite-components", "requires": { "@esri/calcite-design-tokens": "1.0.0", - "@esri/calcite-ui-icons": "3.23.9", + "@esri/calcite-ui-icons": "3.24.1", "@esri/eslint-plugin-calcite-components": "^0.2.3-next.4", "@floating-ui/dom": "1.5.1", "@stencil-community/eslint-plugin": "0.5.0", @@ -42714,9 +42714,9 @@ "dev": true }, "@esri/calcite-ui-icons": { - "version": "3.23.9", - "resolved": "https://registry.npmjs.org/@esri/calcite-ui-icons/-/calcite-ui-icons-3.23.9.tgz", - "integrity": "sha512-9rbdZEV/v2gq7p11KtLxMamamwG9iH2z/8m3y2GFn7HzF9CqOMYP6fwBu4XBjk3uW05+5T6Vksz7Xn9obq9wrg==", + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/@esri/calcite-ui-icons/-/calcite-ui-icons-3.24.1.tgz", + "integrity": "sha512-mPy4i2ivT8d4ytmtK3gOVfo+ldYUIeRRKPgBTExGf5pZBIS6xIu+g5OYqAhTquREivnQBoMMzo2FgDZhKxnuSg==", "dev": true }, "@esri/eslint-plugin-calcite-components": { diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index 1885da75222..b8877918008 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -76,7 +76,7 @@ }, "devDependencies": { "@esri/calcite-design-tokens": "1.0.0", - "@esri/calcite-ui-icons": "3.23.9", + "@esri/calcite-ui-icons": "3.24.1", "@esri/eslint-plugin-calcite-components": "^0.2.3-next.4", "@stencil-community/eslint-plugin": "0.5.0", "@stencil/postcss": "2.1.0", From 700846385fe737913c1db9fb6cc0c5cd06ee650d Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Thu, 31 Aug 2023 09:02:14 -0700 Subject: [PATCH 10/35] fix(list): Stop emitting calciteListChange when a list-item is disabled or closed. (#7624) **Related Issue:** #7627 ## Summary - calciteListChange shouldn't be emitting when an item is disabled or closed. --- packages/calcite-components/src/components/list/list.e2e.ts | 3 +++ packages/calcite-components/src/components/list/list.tsx | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/calcite-components/src/components/list/list.e2e.ts b/packages/calcite-components/src/components/list/list.e2e.ts index c1c94207e81..0dccb24cc46 100755 --- a/packages/calcite-components/src/components/list/list.e2e.ts +++ b/packages/calcite-components/src/components/list/list.e2e.ts @@ -391,10 +391,12 @@ describe("calcite-list", () => { expect(await isElementFocused(page, "#two")).toBe(true); + const calciteListChange = await page.spyOnEvent("calciteListChange"); const listItemThree = await page.find("#three"); listItemThree.setProperty("disabled", false); await page.waitForChanges(); await page.waitForTimeout(listDebounceTimeout); + expect(calciteListChange).toHaveReceivedEventTimes(0); await list.press("ArrowDown"); @@ -404,6 +406,7 @@ describe("calcite-list", () => { listItemFour.setProperty("closed", false); await page.waitForChanges(); await page.waitForTimeout(listDebounceTimeout); + expect(calciteListChange).toHaveReceivedEventTimes(0); await list.press("ArrowDown"); diff --git a/packages/calcite-components/src/components/list/list.tsx b/packages/calcite-components/src/components/list/list.tsx index 391125d83d5..ba60916ca83 100755 --- a/packages/calcite-components/src/components/list/list.tsx +++ b/packages/calcite-components/src/components/list/list.tsx @@ -279,7 +279,7 @@ export class List implements InteractiveComponent, LoadableComponent, SortableCo } event.stopPropagation(); - this.updateListItems(true); + this.updateListItems(); } @Listen("calciteInternalListItemGroupDefaultSlotChange") From 4f5f8b0ff411341ec26eb65053bbd3985b1ebe25 Mon Sep 17 00:00:00 2001 From: JC Franco Date: Thu, 31 Aug 2023 09:54:00 -0700 Subject: [PATCH 11/35] fix(combobox): prevent deselecting items via keyboard in single-persist mode (#7634) **Related Issue:** #4738 ## Summary This also adds test coverage for keyboard toggling for all selection-modes. --- .../src/components/combobox/combobox.e2e.ts | 222 ++++++++++-------- .../src/components/combobox/combobox.tsx | 8 +- 2 files changed, 124 insertions(+), 106 deletions(-) diff --git a/packages/calcite-components/src/components/combobox/combobox.e2e.ts b/packages/calcite-components/src/components/combobox/combobox.e2e.ts index 788aba0288d..4331b0a0004 100644 --- a/packages/calcite-components/src/components/combobox/combobox.e2e.ts +++ b/packages/calcite-components/src/components/combobox/combobox.e2e.ts @@ -378,117 +378,131 @@ describe("calcite-combobox", () => { describe("item selection", () => { describe("toggling items", () => { - it("single-selection mode allows toggling selection once the selected item is clicked", async () => { - const page = await newE2EPage(); - await page.setContent( - html` - - - - - ` - ); - const combobox = await page.find("calcite-combobox"); - const firstOpenEvent = page.waitForEvent("calciteComboboxOpen"); - await combobox.click(); - await firstOpenEvent; - - const item1 = await combobox.find("calcite-combobox-item[value=one]"); - - await item1.click(); - expect(await combobox.getProperty("value")).toBe("one"); - - const secondOpenEvent = page.waitForEvent("calciteComboboxOpen"); - await combobox.click(); - await secondOpenEvent; - - await item1.click(); - expect(await combobox.getProperty("value")).toBe(""); - }); - - it("single-persist-selection mode does not allow toggling selection once the selected item is clicked", async () => { - const page = await newE2EPage(); - await page.setContent( - html` - - - - - ` - ); - const combobox = await page.find("calcite-combobox"); - const firstOpenEvent = page.waitForEvent("calciteComboboxOpen"); - await combobox.click(); - await firstOpenEvent; - - const item1 = await combobox.find("calcite-combobox-item[value=one]"); - - await item1.click(); - expect(await combobox.getProperty("value")).toBe("one"); - - const secondOpenEvent = page.waitForEvent("calciteComboboxOpen"); - await combobox.click(); - await secondOpenEvent; - - await item1.click(); - expect(await combobox.getProperty("value")).toBe("one"); + describe("via keyboard", () => { + assertSelectionModeToggling(async (item): Promise => { + await item.press("Enter"); + }); }); - it("multiple-selection mode allows toggling selection once the selected item is clicked", async () => { - const page = await newE2EPage(); - await page.setContent( - html` - - - - - ` - ); - const combobox = await page.find("calcite-combobox"); - const openEvent = page.waitForEvent("calciteComboboxOpen"); - await combobox.click(); - await openEvent; - - const item1 = await combobox.find("calcite-combobox-item[value=one]"); - - await item1.click(); - expect(await page.find("calcite-combobox >>> calcite-chip")).toBeDefined(); - - await item1.click(); - expect(await page.find("calcite-combobox >>> calcite-chip")).toBeNull(); - - await item1.click(); - expect(await page.find("calcite-combobox >>> calcite-chip")).toBeDefined(); + describe("via mouse", () => { + assertSelectionModeToggling(async (item): Promise => { + await item.click(); + }); }); - it("ancestors-selection mode allows toggling selection once the selected item is clicked", async () => { - const page = await newE2EPage(); - await page.setContent( - html` - - - - - - - ` - ); - const combobox = await page.find("calcite-combobox"); - const openEvent = page.waitForEvent("calciteComboboxOpen"); - await combobox.click(); - await openEvent; - - const item1 = await combobox.find("calcite-combobox-item[value=one]"); + async function assertSelectionModeToggling(selectItem: (item: E2EElement) => Promise): Promise { + it("single-selection mode allows toggling selection once the selected item is selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + ` + ); + const combobox = await page.find("calcite-combobox"); + const firstOpenEvent = page.waitForEvent("calciteComboboxOpen"); + await combobox.click(); + await firstOpenEvent; + + const item1 = await combobox.find("calcite-combobox-item[value=one]"); + + await selectItem(item1); + expect(await combobox.getProperty("value")).toBe("one"); + + const secondOpenEvent = page.waitForEvent("calciteComboboxOpen"); + await combobox.click(); + await secondOpenEvent; + + await selectItem(item1); + expect(await combobox.getProperty("value")).toBe(""); + }); - await item1.click(); - expect(await page.find("calcite-combobox >>> calcite-chip")).toBeDefined(); + it("single-persist-selection mode does not allow toggling selection once the selected item is selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + ` + ); + const combobox = await page.find("calcite-combobox"); + const firstOpenEvent = page.waitForEvent("calciteComboboxOpen"); + await combobox.click(); + await firstOpenEvent; + + const item1 = await combobox.find("calcite-combobox-item[value=one]"); + + await selectItem(item1); + expect(await combobox.getProperty("value")).toBe("one"); + + const secondOpenEvent = page.waitForEvent("calciteComboboxOpen"); + await combobox.click(); + await secondOpenEvent; + + await selectItem(item1); + expect(await combobox.getProperty("value")).toBe("one"); + }); - await item1.click(); - expect(await page.find("calcite-combobox >>> calcite-chip")).toBeNull(); + it("multiple-selection mode allows toggling selection once the selected item is selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + ` + ); + const combobox = await page.find("calcite-combobox"); + const openEvent = page.waitForEvent("calciteComboboxOpen"); + await combobox.click(); + await openEvent; + + const item1 = await combobox.find("calcite-combobox-item[value=one]"); + + await selectItem(item1); + expect(await page.find("calcite-combobox >>> calcite-chip")).toBeDefined(); + + await selectItem(item1); + expect(await page.find("calcite-combobox >>> calcite-chip")).toBeNull(); + + await selectItem(item1); + expect(await page.find("calcite-combobox >>> calcite-chip")).toBeDefined(); + }); - await item1.click(); - expect(await page.find("calcite-combobox >>> calcite-chip")).toBeDefined(); - }); + it("ancestors-selection mode allows toggling selection once the selected item is selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + + ` + ); + const combobox = await page.find("calcite-combobox"); + const openEvent = page.waitForEvent("calciteComboboxOpen"); + await combobox.click(); + await openEvent; + + const item1 = await combobox.find("calcite-combobox-item[value=one]"); + + await selectItem(item1); + expect(await page.find("calcite-combobox >>> calcite-chip")).toBeDefined(); + + await selectItem(item1); + expect(await page.find("calcite-combobox >>> calcite-chip")).toBeNull(); + + await selectItem(item1); + expect(await page.find("calcite-combobox >>> calcite-chip")).toBeDefined(); + }); + } }); it("should select parent in ancestor selection mode", async () => { diff --git a/packages/calcite-components/src/components/combobox/combobox.tsx b/packages/calcite-components/src/components/combobox/combobox.tsx index fb7109565bd..b3d352af689 100644 --- a/packages/calcite-components/src/components/combobox/combobox.tsx +++ b/packages/calcite-components/src/components/combobox/combobox.tsx @@ -890,7 +890,10 @@ export class Combobox private emitComboboxChange = debounce(this.internalComboboxChangeEvent, 0); toggleSelection(item: HTMLCalciteComboboxItemElement, value = !item.selected): void { - if (!item) { + if ( + !item || + (this.selectionMode === "single-persist" && item.selected && item.value === this.value) + ) { return; } @@ -907,6 +910,7 @@ export class Combobox this.ignoreSelectedEventsFlag = false; this.selectedItems = this.getSelectedItems(); this.emitComboboxChange(); + if (this.textInput) { this.textInput.value = item.textLabel; } @@ -1135,7 +1139,7 @@ export class Combobox } isMulti(): boolean { - return this.selectionMode !== "single" && this.selectionMode !== "single-persist"; + return !isSingleLike(this.selectionMode); } comboboxFocusHandler = (): void => { From fe2e70b2a5cba11953c402f7c5e416090b3cac5f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 31 Aug 2023 17:20:39 +0000 Subject: [PATCH 12/35] chore: release next --- package-lock.json | 8 ++++---- packages/calcite-components-react/CHANGELOG.md | 4 ++++ packages/calcite-components-react/package.json | 4 ++-- packages/calcite-components/CHANGELOG.md | 7 +++++++ packages/calcite-components/package.json | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9cb03ab3fc2..b0fe36ee11b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40443,7 +40443,7 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "1.7.0-next.15", + "version": "1.7.0-next.16", "license": "SEE LICENSE.md", "dependencies": { "@floating-ui/dom": "1.5.1", @@ -40470,10 +40470,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "1.7.0-next.15", + "version": "1.7.0-next.16", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^1.7.0-next.15" + "@esri/calcite-components": "^1.7.0-next.16" }, "peerDependencies": { "react": ">=16.7", @@ -42704,7 +42704,7 @@ "@esri/calcite-components-react": { "version": "file:packages/calcite-components-react", "requires": { - "@esri/calcite-components": "^1.7.0-next.15" + "@esri/calcite-components": "^1.7.0-next.16" } }, "@esri/calcite-design-tokens": { diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index 7896d751dcf..dca78a71fc9 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.16](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.15...@esri/calcite-components-react@1.7.0-next.16) (2023-08-31) + +**Note:** Version bump only for package @esri/calcite-components-react + ## [1.7.0-next.15](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.14...@esri/calcite-components-react@1.7.0-next.15) (2023-08-31) **Note:** Version bump only for package @esri/calcite-components-react diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index d139712d377..ce5639a6c95 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,7 +1,7 @@ { "name": "@esri/calcite-components-react", "sideEffects": false, - "version": "1.7.0-next.15", + "version": "1.7.0-next.16", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", @@ -20,7 +20,7 @@ "dist/" ], "dependencies": { - "@esri/calcite-components": "^1.7.0-next.15" + "@esri/calcite-components": "^1.7.0-next.16" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index f7a1ae9e6a1..7e4a8a0e5d2 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.16](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.15...@esri/calcite-components@1.7.0-next.16) (2023-08-31) + +### Bug Fixes + +- **combobox:** prevent deselecting items via keyboard in single-persist mode ([#7634](https://github.com/Esri/calcite-design-system/issues/7634)) ([4f5f8b0](https://github.com/Esri/calcite-design-system/commit/4f5f8b0ff411341ec26eb65053bbd3985b1ebe25)), closes [#4738](https://github.com/Esri/calcite-design-system/issues/4738) +- **list:** Stop emitting calciteListChange when a list-item is disabled or closed. ([#7624](https://github.com/Esri/calcite-design-system/issues/7624)) ([7008463](https://github.com/Esri/calcite-design-system/commit/700846385fe737913c1db9fb6cc0c5cd06ee650d)), closes [#7627](https://github.com/Esri/calcite-design-system/issues/7627) + ## [1.7.0-next.15](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.14...@esri/calcite-components@1.7.0-next.15) (2023-08-31) ### Features diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index b8877918008..dc6520793e6 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "1.7.0-next.15", + "version": "1.7.0-next.16", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", From b067e728f999931f234f2b1b07af425c9bb11329 Mon Sep 17 00:00:00 2001 From: Adam Tirella Date: Thu, 31 Aug 2023 11:42:35 -0700 Subject: [PATCH 13/35] feat(table): Add Table and related components (#7607) **Related Issue:** #6697 ## Summary Adds Table and related components --- .../assets/table-cell/t9n/messages.json | 7 + .../assets/table-cell/t9n/messages_ar.json | 7 + .../assets/table-cell/t9n/messages_bg.json | 7 + .../assets/table-cell/t9n/messages_bs.json | 7 + .../assets/table-cell/t9n/messages_ca.json | 7 + .../assets/table-cell/t9n/messages_cs.json | 7 + .../assets/table-cell/t9n/messages_da.json | 7 + .../assets/table-cell/t9n/messages_de.json | 7 + .../assets/table-cell/t9n/messages_el.json | 7 + .../assets/table-cell/t9n/messages_en.json | 7 + .../assets/table-cell/t9n/messages_es.json | 7 + .../assets/table-cell/t9n/messages_et.json | 7 + .../assets/table-cell/t9n/messages_fi.json | 7 + .../assets/table-cell/t9n/messages_fr.json | 7 + .../assets/table-cell/t9n/messages_he.json | 7 + .../assets/table-cell/t9n/messages_hr.json | 7 + .../assets/table-cell/t9n/messages_hu.json | 7 + .../assets/table-cell/t9n/messages_id.json | 7 + .../assets/table-cell/t9n/messages_it.json | 7 + .../assets/table-cell/t9n/messages_ja.json | 7 + .../assets/table-cell/t9n/messages_ko.json | 7 + .../assets/table-cell/t9n/messages_lt.json | 7 + .../assets/table-cell/t9n/messages_lv.json | 7 + .../assets/table-cell/t9n/messages_nl.json | 7 + .../assets/table-cell/t9n/messages_no.json | 7 + .../assets/table-cell/t9n/messages_pl.json | 7 + .../assets/table-cell/t9n/messages_pt-BR.json | 7 + .../assets/table-cell/t9n/messages_pt-PT.json | 7 + .../assets/table-cell/t9n/messages_ro.json | 7 + .../assets/table-cell/t9n/messages_ru.json | 7 + .../assets/table-cell/t9n/messages_sk.json | 7 + .../assets/table-cell/t9n/messages_sl.json | 7 + .../assets/table-cell/t9n/messages_sr.json | 7 + .../assets/table-cell/t9n/messages_sv.json | 7 + .../assets/table-cell/t9n/messages_th.json | 7 + .../assets/table-cell/t9n/messages_tr.json | 7 + .../assets/table-cell/t9n/messages_uk.json | 7 + .../assets/table-cell/t9n/messages_vi.json | 7 + .../assets/table-cell/t9n/messages_zh-CN.json | 7 + .../assets/table-cell/t9n/messages_zh-HK.json | 7 + .../assets/table-cell/t9n/messages_zh-TW.json | 7 + .../src/components/table-cell/readme.md | 55 + .../src/components/table-cell/resources.ts | 7 + .../src/components/table-cell/table-cell.scss | 77 + .../src/components/table-cell/table-cell.tsx | 238 + .../assets/table-header/t9n/messages.json | 8 + .../assets/table-header/t9n/messages_ar.json | 8 + .../assets/table-header/t9n/messages_bg.json | 8 + .../assets/table-header/t9n/messages_bs.json | 8 + .../assets/table-header/t9n/messages_ca.json | 8 + .../assets/table-header/t9n/messages_cs.json | 8 + .../assets/table-header/t9n/messages_da.json | 8 + .../assets/table-header/t9n/messages_de.json | 8 + .../assets/table-header/t9n/messages_el.json | 8 + .../assets/table-header/t9n/messages_en.json | 8 + .../assets/table-header/t9n/messages_es.json | 8 + .../assets/table-header/t9n/messages_et.json | 8 + .../assets/table-header/t9n/messages_fi.json | 8 + .../assets/table-header/t9n/messages_fr.json | 8 + .../assets/table-header/t9n/messages_he.json | 8 + .../assets/table-header/t9n/messages_hr.json | 8 + .../assets/table-header/t9n/messages_hu.json | 8 + .../assets/table-header/t9n/messages_id.json | 8 + .../assets/table-header/t9n/messages_it.json | 8 + .../assets/table-header/t9n/messages_ja.json | 8 + .../assets/table-header/t9n/messages_ko.json | 8 + .../assets/table-header/t9n/messages_lt.json | 8 + .../assets/table-header/t9n/messages_lv.json | 8 + .../assets/table-header/t9n/messages_nl.json | 8 + .../assets/table-header/t9n/messages_no.json | 8 + .../assets/table-header/t9n/messages_pl.json | 8 + .../table-header/t9n/messages_pt-BR.json | 8 + .../table-header/t9n/messages_pt-PT.json | 8 + .../assets/table-header/t9n/messages_ro.json | 8 + .../assets/table-header/t9n/messages_ru.json | 8 + .../assets/table-header/t9n/messages_sk.json | 8 + .../assets/table-header/t9n/messages_sl.json | 8 + .../assets/table-header/t9n/messages_sr.json | 8 + .../assets/table-header/t9n/messages_sv.json | 8 + .../assets/table-header/t9n/messages_th.json | 8 + .../assets/table-header/t9n/messages_tr.json | 8 + .../assets/table-header/t9n/messages_uk.json | 8 + .../assets/table-header/t9n/messages_vi.json | 8 + .../table-header/t9n/messages_zh-CN.json | 8 + .../table-header/t9n/messages_zh-HK.json | 8 + .../table-header/t9n/messages_zh-TW.json | 8 + .../src/components/table-header/readme.md | 63 + .../src/components/table-header/resources.ts | 11 + .../components/table-header/table-header.scss | 77 + .../components/table-header/table-header.tsx | 239 + .../src/components/table-row/readme.md | 53 + .../src/components/table-row/table-row.scss | 27 + .../src/components/table-row/table-row.tsx | 393 ++ .../table/assets/table/t9n/messages.json | 7 + .../table/assets/table/t9n/messages_ar.json | 7 + .../table/assets/table/t9n/messages_bg.json | 7 + .../table/assets/table/t9n/messages_bs.json | 7 + .../table/assets/table/t9n/messages_ca.json | 7 + .../table/assets/table/t9n/messages_cs.json | 7 + .../table/assets/table/t9n/messages_da.json | 7 + .../table/assets/table/t9n/messages_de.json | 7 + .../table/assets/table/t9n/messages_el.json | 7 + .../table/assets/table/t9n/messages_en.json | 7 + .../table/assets/table/t9n/messages_es.json | 7 + .../table/assets/table/t9n/messages_et.json | 7 + .../table/assets/table/t9n/messages_fi.json | 7 + .../table/assets/table/t9n/messages_fr.json | 7 + .../table/assets/table/t9n/messages_he.json | 7 + .../table/assets/table/t9n/messages_hr.json | 7 + .../table/assets/table/t9n/messages_hu.json | 7 + .../table/assets/table/t9n/messages_id.json | 7 + .../table/assets/table/t9n/messages_it.json | 7 + .../table/assets/table/t9n/messages_ja.json | 7 + .../table/assets/table/t9n/messages_ko.json | 7 + .../table/assets/table/t9n/messages_lt.json | 7 + .../table/assets/table/t9n/messages_lv.json | 7 + .../table/assets/table/t9n/messages_nl.json | 7 + .../table/assets/table/t9n/messages_no.json | 7 + .../table/assets/table/t9n/messages_pl.json | 7 + .../assets/table/t9n/messages_pt-BR.json | 7 + .../assets/table/t9n/messages_pt-PT.json | 7 + .../table/assets/table/t9n/messages_ro.json | 7 + .../table/assets/table/t9n/messages_ru.json | 7 + .../table/assets/table/t9n/messages_sk.json | 7 + .../table/assets/table/t9n/messages_sl.json | 7 + .../table/assets/table/t9n/messages_sr.json | 7 + .../table/assets/table/t9n/messages_sv.json | 7 + .../table/assets/table/t9n/messages_th.json | 7 + .../table/assets/table/t9n/messages_tr.json | 7 + .../table/assets/table/t9n/messages_uk.json | 7 + .../table/assets/table/t9n/messages_vi.json | 7 + .../assets/table/t9n/messages_zh-CN.json | 7 + .../assets/table/t9n/messages_zh-HK.json | 7 + .../assets/table/t9n/messages_zh-TW.json | 7 + .../src/components/table/interfaces.ts | 14 + .../src/components/table/readme.md | 50 + .../src/components/table/resources.ts | 17 + .../src/components/table/table.e2e.ts | 2427 ++++++++++ .../src/components/table/table.scss | 92 + .../src/components/table/table.stories.ts | 912 ++++ .../src/components/table/table.tsx | 525 +++ .../src/components/table/usage/Advanced.md | 70 + .../src/components/table/usage/Basic.md | 30 + .../calcite-components/src/demos/table.html | 3990 +++++++++++++++++ packages/calcite-components/src/index.html | 6 + t9nmanifest.txt | 3 + 146 files changed, 10278 insertions(+) create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ar.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_bg.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_bs.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ca.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_cs.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_da.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_de.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_el.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_en.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_es.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_et.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_fi.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_fr.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_he.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_hr.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_hu.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_id.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_it.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ja.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ko.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_lt.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_lv.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_nl.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_no.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pl.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pt-BR.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pt-PT.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ro.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ru.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sk.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sl.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sr.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sv.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_th.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_tr.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_uk.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_vi.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-CN.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-HK.json create mode 100644 packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-TW.json create mode 100644 packages/calcite-components/src/components/table-cell/readme.md create mode 100644 packages/calcite-components/src/components/table-cell/resources.ts create mode 100644 packages/calcite-components/src/components/table-cell/table-cell.scss create mode 100644 packages/calcite-components/src/components/table-cell/table-cell.tsx create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ar.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_bg.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_bs.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ca.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_cs.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_da.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_de.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_el.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_en.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_es.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_et.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_fi.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_fr.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_he.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_hr.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_hu.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_id.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_it.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ja.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ko.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_lt.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_lv.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_nl.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_no.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pl.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pt-BR.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pt-PT.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ro.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ru.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sk.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sl.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sr.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sv.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_th.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_tr.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_uk.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_vi.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-CN.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-HK.json create mode 100644 packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-TW.json create mode 100644 packages/calcite-components/src/components/table-header/readme.md create mode 100644 packages/calcite-components/src/components/table-header/resources.ts create mode 100644 packages/calcite-components/src/components/table-header/table-header.scss create mode 100644 packages/calcite-components/src/components/table-header/table-header.tsx create mode 100644 packages/calcite-components/src/components/table-row/readme.md create mode 100644 packages/calcite-components/src/components/table-row/table-row.scss create mode 100644 packages/calcite-components/src/components/table-row/table-row.tsx create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_ar.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_bg.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_bs.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_ca.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_cs.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_da.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_de.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_el.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_en.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_es.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_et.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_fi.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_fr.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_he.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_hr.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_hu.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_id.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_it.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_ja.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_ko.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_lt.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_lv.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_nl.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_no.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_pl.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_pt-BR.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_pt-PT.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_ro.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_ru.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_sk.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_sl.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_sr.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_sv.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_th.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_tr.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_uk.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_vi.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-CN.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-HK.json create mode 100644 packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-TW.json create mode 100644 packages/calcite-components/src/components/table/interfaces.ts create mode 100644 packages/calcite-components/src/components/table/readme.md create mode 100644 packages/calcite-components/src/components/table/resources.ts create mode 100644 packages/calcite-components/src/components/table/table.e2e.ts create mode 100644 packages/calcite-components/src/components/table/table.scss create mode 100644 packages/calcite-components/src/components/table/table.stories.ts create mode 100644 packages/calcite-components/src/components/table/table.tsx create mode 100644 packages/calcite-components/src/components/table/usage/Advanced.md create mode 100644 packages/calcite-components/src/components/table/usage/Basic.md create mode 100644 packages/calcite-components/src/demos/table.html diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ar.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ar.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ar.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_bg.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_bg.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_bg.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_bs.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_bs.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_bs.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ca.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ca.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ca.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_cs.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_cs.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_cs.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_da.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_da.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_da.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_de.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_de.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_de.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_el.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_el.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_el.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_en.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_en.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_en.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_es.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_es.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_es.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_et.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_et.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_et.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_fi.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_fi.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_fi.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_fr.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_fr.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_fr.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_he.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_he.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_he.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_hr.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_hr.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_hr.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_hu.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_hu.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_hu.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_id.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_id.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_id.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_it.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_it.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_it.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ja.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ja.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ja.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ko.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ko.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ko.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_lt.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_lt.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_lt.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_lv.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_lv.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_lv.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_nl.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_nl.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_nl.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_no.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_no.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_no.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pl.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pl.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pl.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pt-BR.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pt-BR.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pt-BR.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pt-PT.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pt-PT.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_pt-PT.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ro.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ro.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ro.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ru.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ru.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_ru.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sk.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sk.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sk.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sl.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sl.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sl.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sr.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sr.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sr.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sv.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sv.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_sv.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_th.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_th.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_th.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_tr.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_tr.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_tr.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_uk.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_uk.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_uk.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_vi.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_vi.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_vi.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-CN.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-CN.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-CN.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-HK.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-HK.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-HK.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-TW.json b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-TW.json new file mode 100644 index 00000000000..d8845d0a69a --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/assets/table-cell/t9n/messages_zh-TW.json @@ -0,0 +1,7 @@ +{ + "keyboardDeselect": "Press Enter or Space to deselect row", + "keyboardSelect": "Press Enter or Space to select row", + "row": "row", + "selected": "selected", + "unselected": "unselected" +} diff --git a/packages/calcite-components/src/components/table-cell/readme.md b/packages/calcite-components/src/components/table-cell/readme.md new file mode 100644 index 00000000000..7c9c20fd6bc --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/readme.md @@ -0,0 +1,55 @@ +# calcite-table-cell + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ------------------ | ----------- | ----------------------------------------------------------------------------------------- | ------------------------------ | ----------- | +| `alignment` | `alignment` | Specifies the alignment of the component. | `"center" \| "end" \| "start"` | `"start"` | +| `colSpan` | `col-span` | | `number` | `undefined` | +| `messageOverrides` | -- | Use this property to override individual strings used by the component. | `{ focusReadout?: string; }` | `undefined` | +| `rowSpan` | `row-span` | | `number` | `undefined` | +| `scale` | `scale` | Specifies the size of the component. | `"l" \| "m" \| "s"` | `"m"` | +| `value` | `value` | Provide a value to the component - used to sort when table header is sortable and active. | `""` | `undefined` | + +## Methods + +### `setFocus() => Promise` + +Sets focus on the component's first focusable element. + +#### Returns + +Type: `Promise` + +## Slots + +| Slot | Description | +| ---- | ------------------------------------------------ | +| | A slot for adding content, usually text content. | + +## CSS Custom Properties + +| Name | Description | +| ----------------------------------- | ------------------------------------------------ | +| `--calcite-table-cell-background` | Specifies the background color of the component. | +| `--calcite-table-cell-border-color` | Specifies the border color of the component. | + +## Dependencies + +### Used by + +- [calcite-table-row](../table-row) + +### Graph + +```mermaid +graph TD; + calcite-table-row --> calcite-table-cell + style calcite-table-cell fill:#f9f,stroke:#333,stroke-width:4px +``` + +--- + +_Built with [StencilJS](https://stenciljs.com/)_ diff --git a/packages/calcite-components/src/components/table-cell/resources.ts b/packages/calcite-components/src/components/table-cell/resources.ts new file mode 100644 index 00000000000..14f52645e9b --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/resources.ts @@ -0,0 +1,7 @@ +export const CSS = { + numberCell: "number-cell", + footerCell: "footer-cell", + selectionCell: "selection-cell", + selectedCell: "selected-cell", + assistiveText: "assistive-text", +}; diff --git a/packages/calcite-components/src/components/table-cell/table-cell.scss b/packages/calcite-components/src/components/table-cell/table-cell.scss new file mode 100644 index 00000000000..66f70c60228 --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/table-cell.scss @@ -0,0 +1,77 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-table-cell-background: Specifies the background color of the component. + * @prop --calcite-table-cell-border-color: Specifies the border color of the component. + */ + +:host { + --calcite-internal-table-cell-background-internal: var(--calcite-table-cell-background, transparent); + --calcite-internal-table-cell-border-color-internal: var(--calcite-table-cell-border-color, transparent); + @apply contents; +} + +:host([alignment="center"]) td { + @apply text-center; +} + +:host([alignment="end"]) td { + @apply text-end; +} + +.assistive-text { + @apply sr-only; +} + +td { + @apply text-start focus-base align-middle text-color-1; + background: var(--calcite-internal-table-cell-background); + border-inline-end: 1px solid var(--calcite-ui-border-3); + font-size: var(--calcite-internal-table-cell-font-size); + &:focus { + @apply focus-inset; + } + padding: var(--calcite-internal-table-cell-padding); +} + +.number-cell { + @apply bg-foreground-2; +} + +.footer-cell { + @apply bg-background font-medium text-color-1; + border-block-start: 1px solid var(--calcite-ui-border-3); +} + +.number-cell, +.selection-cell { + border-inline-end: 1px solid var(--calcite-ui-border-3); + inline-size: 2rem; + min-inline-size: 2rem; +} + +.selection-cell { + @apply cursor-pointer text-color-3; + inset-inline-start: 2rem; +} + +.selected-cell:not(.number-cell):not(.footer-cell) { + --calcite-table-cell-background: var(--calcite-ui-foreground-current); + background: var(--calcite-ui-foreground-current); +} + +.selection-cell.selected-cell { + box-shadow: inset 0.25rem 0 0 0 var(--calcite-ui-brand); + color: var(--calcite-ui-brand); + & calcite-icon { + color: var(--calcite-ui-brand); + } +} +.selection-cell { + @apply align-middle; + & ::slotted(calcite-icon) { + @apply pointer-events-none mt-1; + } +} diff --git a/packages/calcite-components/src/components/table-cell/table-cell.tsx b/packages/calcite-components/src/components/table-cell/table-cell.tsx new file mode 100644 index 00000000000..15d34f01ca7 --- /dev/null +++ b/packages/calcite-components/src/components/table-cell/table-cell.tsx @@ -0,0 +1,238 @@ +import { Component, Element, h, Host, Method, Prop, State, VNode, Watch } from "@stencil/core"; +import { Alignment, Scale } from "../interfaces"; +import { + componentFocusable, + LoadableComponent, + setComponentLoaded, + setUpLoadableComponent, +} from "../../utils/loadable"; +import { + connectMessages, + disconnectMessages, + setUpMessages, + T9nComponent, + updateMessages, +} from "../../utils/t9n"; +import { + connectInteractive, + disconnectInteractive, + InteractiveComponent, + updateHostInteraction, +} from "../../utils/interactive"; + +import { connectLocalized, disconnectLocalized, LocalizedComponent } from "../../utils/locale"; +import { TableCellMessages } from "./assets/table-cell/t9n"; +import { CSS } from "./resources"; +import { RowType } from "../table/interfaces"; + +/** + * @slot - A slot for adding content, usually text content. + */ +@Component({ + tag: "calcite-table-cell", + styleUrl: "table-cell.scss", + shadow: true, + assetsDirs: ["assets"], +}) +export class TableCell + implements InteractiveComponent, LocalizedComponent, LoadableComponent, T9nComponent +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** Specifies the alignment of the component. */ + @Prop({ reflect: true }) alignment: Alignment = "start"; + + /** Specifies the number of columns the component should span. */ + @Prop({ reflect: true }) colSpan: number; + + /** Specifies the number of rows the component should span. */ + @Prop({ reflect: true }) rowSpan: number; + + /** @internal */ + @Prop() disabled: boolean; + + /** @internal */ + @Prop() numberCell: boolean; + + /** @internal */ + @Prop() parentRowIsSelected: boolean; + + @Watch("parentRowIsSelected") + onSelectedChange(): void { + this.updateScreenReaderSelectionText(); + } + + /** @internal */ + @Prop() parentRowPositionLocalized: string; + + /** @internal */ + @Prop() parentRowType: RowType; + + /** @internal */ + @Prop() positionInRow: number; + + /** @internal */ + @Prop() readCellContentsToAT: boolean; + + /** @internal */ + @Prop() scale: Scale = "m"; + + /** @internal */ + @Prop() selectionCell: boolean; + + /** + * Made into a prop for testing purposes only + * + * @internal + */ + // eslint-disable-next-line @stencil-community/strict-mutable -- updated by t9n module + @Prop({ mutable: true }) messages: TableCellMessages; + + /** + * Use this property to override individual strings used by the component. + */ + // eslint-disable-next-line @stencil-community/strict-mutable -- updated by t9n module + @Prop({ mutable: true }) messageOverrides: Partial; + + @Watch("messageOverrides") + onMessagesChange(): void { + /* wired up by t9n util */ + } + + // -------------------------------------------------------------------------- + // + // Private Properties + // + // -------------------------------------------------------------------------- + + @Element() el: HTMLCalciteTableCellElement; + + @State() contentsText = ""; + + @State() defaultMessages: TableCellMessages; + + @State() focused = false; + + @State() selectionText = ""; + + @State() effectiveLocale = ""; + + @Watch("effectiveLocale") + effectiveLocaleChange(): void { + updateMessages(this, this.effectiveLocale); + } + + private containerEl: HTMLTableCellElement; + + // -------------------------------------------------------------------------- + // + // Lifecycle + // + // -------------------------------------------------------------------------- + + async componentWillLoad(): Promise { + setUpLoadableComponent(this); + await setUpMessages(this); + this.updateScreenReaderContentsText(); + this.updateScreenReaderSelectionText(); + } + + componentDidLoad(): void { + setComponentLoaded(this); + } + + connectedCallback(): void { + connectLocalized(this); + connectMessages(this); + connectInteractive(this); + } + + componentDidRender(): void { + updateHostInteraction(this); + } + + disconnectedCallback(): void { + disconnectLocalized(this); + disconnectMessages(this); + disconnectInteractive(this); + } + + //-------------------------------------------------------------------------- + // + // Public Methods + // + // -------------------------------------------------------------------------- + + /** Sets focus on the component. */ + @Method() + async setFocus(): Promise { + await componentFocusable(this); + this.containerEl.focus(); + } + + // -------------------------------------------------------------------------- + // + // Private Methods + // + // -------------------------------------------------------------------------- + + private updateScreenReaderSelectionText(): void { + const selectedText = `${this.messages?.row} ${this.parentRowPositionLocalized} ${this.messages?.selected} ${this.messages?.keyboardDeselect}`; + const unselectedText = `${this.messages?.row} ${this.parentRowPositionLocalized} ${this.messages?.unselected} ${this.messages?.keyboardSelect}`; + this.selectionText = this.parentRowIsSelected ? selectedText : unselectedText; + } + + private updateScreenReaderContentsText = (): void => { + this.contentsText = this.el.textContent; + }; + + private onContainerBlur = (): void => { + this.focused = false; + }; + + private onContainerFocus = (): void => { + this.focused = true; + }; + + //-------------------------------------------------------------------------- + // + // Render Methods + // + //-------------------------------------------------------------------------- + + render(): VNode { + return ( + + (this.containerEl = el)} + > + {(this.selectionCell || this.readCellContentsToAT) && this.focused && ( + + {this.selectionCell && this.selectionText} + {this.readCellContentsToAT && !this.selectionCell && this.contentsText} + + )} + + + + ); + } +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ar.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ar.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ar.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_bg.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_bg.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_bg.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_bs.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_bs.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_bs.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ca.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ca.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ca.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_cs.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_cs.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_cs.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_da.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_da.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_da.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_de.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_de.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_de.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_el.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_el.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_el.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_en.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_en.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_en.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_es.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_es.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_es.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_et.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_et.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_et.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_fi.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_fi.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_fi.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_fr.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_fr.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_fr.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_he.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_he.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_he.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_hr.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_hr.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_hr.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_hu.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_hu.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_hu.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_id.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_id.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_id.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_it.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_it.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_it.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ja.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ja.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ja.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ko.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ko.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ko.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_lt.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_lt.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_lt.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_lv.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_lv.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_lv.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_nl.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_nl.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_nl.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_no.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_no.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_no.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pl.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pl.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pl.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pt-BR.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pt-BR.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pt-BR.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pt-PT.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pt-PT.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_pt-PT.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ro.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ro.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ro.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ru.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ru.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_ru.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sk.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sk.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sk.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sl.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sl.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sl.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sr.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sr.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sr.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sv.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sv.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_sv.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_th.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_th.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_th.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_tr.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_tr.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_tr.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_uk.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_uk.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_uk.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_vi.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_vi.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_vi.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-CN.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-CN.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-CN.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-HK.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-HK.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-HK.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-TW.json b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-TW.json new file mode 100644 index 00000000000..b73b48a09cf --- /dev/null +++ b/packages/calcite-components/src/components/table-header/assets/table-header/t9n/messages_zh-TW.json @@ -0,0 +1,8 @@ +{ + "all": "all", + "keyboardDeselectAll": "Press Enter or Space to deselect all rows", + "keyboardSelectAll": "Press Enter or Space to select all rows", + "rowNumber": "Row number", + "selected": "rows selected", + "selectionColumn": "Selection column" +} diff --git a/packages/calcite-components/src/components/table-header/readme.md b/packages/calcite-components/src/components/table-header/readme.md new file mode 100644 index 00000000000..308975e0e20 --- /dev/null +++ b/packages/calcite-components/src/components/table-header/readme.md @@ -0,0 +1,63 @@ +# calcite-table-header + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ------------------ | ------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ----------- | +| `alignment` | `alignment` | Specifies the alignment of the component. | `"center" \| "end" \| "start"` | `"start"` | +| `colSpan` | `col-span` | | `number` | `undefined` | +| `description` | `description` | A description to display beneath heading content. | `string` | `undefined` | +| `heading` | `heading` | A heading to display above description content. | `string` | `undefined` | +| `label` | `label` | | `string` | `""` | +| `messageOverrides` | -- | Use this property to override individual strings used by the component. | `{ focusReadout?: string; sortAscending?: string; sortDescending?: string; sortNone?: string; }` | `undefined` | +| `rowSpan` | `row-span` | | `number` | `undefined` | +| `scale` | `scale` | Specifies the size of the component. | `"l" \| "m" \| "s"` | `"m"` | +| `sortable` | `sortable` | Specifies if the component should be able to sort associated `calcite-table-cell` ascending or descending | `boolean` | `false` | + +## Methods + +### `setFocus() => Promise` + +Sets focus on the component's first focusable element. + +#### Returns + +Type: `Promise` + +## Slots + +| Slot | Description | +| --------------- | ------------------------------------------------ | +| `"actions-end"` | A slot for adding content, usually text content. | + +## CSS Custom Properties + +| Name | Description | +| ------------------------------------- | ------------------------------------------------ | +| `--calcite-table-header-background` | Specifies the background color of the component. | +| `--calcite-table-header-border-color` | Specifies the border color of the component. | + +## Dependencies + +### Used by + +- [calcite-table-row](../table-row) + +### Depends on + +- [calcite-icon](../icon) + +### Graph + +```mermaid +graph TD; + calcite-table-header --> calcite-icon + calcite-table-row --> calcite-table-header + style calcite-table-header fill:#f9f,stroke:#333,stroke-width:4px +``` + +--- + +_Built with [StencilJS](https://stenciljs.com/)_ diff --git a/packages/calcite-components/src/components/table-header/resources.ts b/packages/calcite-components/src/components/table-header/resources.ts new file mode 100644 index 00000000000..23a28bf059d --- /dev/null +++ b/packages/calcite-components/src/components/table-header/resources.ts @@ -0,0 +1,11 @@ +export const CSS = { + numberCell: "number-cell", + selectionCell: "selection-cell", + bodyRow: "body-row", + footerRow: "footer-row", + heading: "heading", + description: "description", + multipleSelectionCell: "cell--multiple-selection", + assistiveText: "assistive-text", + active: "active", +}; diff --git a/packages/calcite-components/src/components/table-header/table-header.scss b/packages/calcite-components/src/components/table-header/table-header.scss new file mode 100644 index 00000000000..bd167b5d4ea --- /dev/null +++ b/packages/calcite-components/src/components/table-header/table-header.scss @@ -0,0 +1,77 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-table-header-background: Specifies the background color of the component. + * @prop --calcite-table-header-border-color: Specifies the border color of the component. + */ + +:host { + --calcite-internal-table-header-background: var(--calcite-table-header-background, var(--calcite-ui-foreground-2)); + --calcite-internal-table-header-border-color: var(--calcite-table-border-color, var(--calcite-ui-border-3)); + @apply contents; +} + +:host([alignment="center"]) th { + @apply text-center; +} + +:host([alignment="end"]) th { + @apply text-end; +} + +.assistive-text { + @apply sr-only; +} + +th { + @apply text-color-1 focus-base text-start font-medium align-top; + font-size: var(--calcite-internal-table-cell-font-size); + border-inline-end: 1px solid var(--calcite-internal-table-header-border-color); + border-block-end: 1px solid var(--calcite-internal-table-header-border-color); + padding-block: calc(var(--calcite-internal-table-cell-padding) * 1.5); + padding-inline: var(--calcite-internal-table-cell-padding); + background-color: var(--calcite-internal-table-header-background); + &:focus-within { + @apply focus-inset; + } +} + +th.body-row, +th.footer-row { + @apply align-middle; + border-block-end: 0; +} + +th.footer-row { + border-block-start: 1px solid var(--calcite-internal-table-header-border-color); +} + +.cell--multiple-selection { + @apply cursor-pointer align-middle text-color-3; +} + +.number-cell, +.selection-cell { + @apply text-color-2; + inline-size: 2rem; + min-inline-size: 2rem; +} + +.selection-cell calcite-icon.active { + color: var(--calcite-ui-brand); +} +.number-cell calcite-icon, +.selection-cell calcite-icon { + @apply ms-auto me-auto align-middle; +} + +.heading { + @apply text-color-1; +} + +.description { + @apply text-color-3; + font-size: var(--calcite-internal-table-cell-font-size-secondary); +} diff --git a/packages/calcite-components/src/components/table-header/table-header.tsx b/packages/calcite-components/src/components/table-header/table-header.tsx new file mode 100644 index 00000000000..f28eede7402 --- /dev/null +++ b/packages/calcite-components/src/components/table-header/table-header.tsx @@ -0,0 +1,239 @@ +import { Component, Element, h, Host, Method, Prop, State, VNode, Watch } from "@stencil/core"; +import { + componentFocusable, + LoadableComponent, + setComponentLoaded, + setUpLoadableComponent, +} from "../../utils/loadable"; +import { + connectMessages, + disconnectMessages, + setUpMessages, + T9nComponent, + updateMessages, +} from "../../utils/t9n"; +import { connectLocalized, disconnectLocalized, LocalizedComponent } from "../../utils/locale"; +import { Alignment, Scale, SelectionMode } from "../interfaces"; +import { TableHeaderMessages } from "./assets/table-header/t9n"; +import { CSS } from "./resources"; +import { RowType } from "../table/interfaces"; + +@Component({ + tag: "calcite-table-header", + styleUrl: "table-header.scss", + shadow: true, + assetsDirs: ["assets"], +}) +export class TableHeader implements LocalizedComponent, LoadableComponent, T9nComponent { + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** Specifies the alignment of the component. */ + @Prop({ reflect: true }) alignment: Alignment = "start"; + + /** Specifies the number of columns the component should span. */ + @Prop({ reflect: true }) colSpan: number; + + /** A description to display beneath heading content. */ + @Prop({ reflect: true }) description: string; + + /** A heading to display above description content. */ + @Prop({ reflect: true }) heading: string; + + /** Specifies the number of rows the component should span. */ + @Prop({ reflect: true }) rowSpan: number; + + /** @internal */ + @Prop() numberCell = false; + + /** @internal */ + @Prop() parentRowPosition: number; + + /** @internal */ + @Prop() parentRowType: RowType; + + /** @internal */ + @Prop() positionInRow: number; + + /** @internal */ + @Prop() scale: Scale; + + /** @internal */ + @Prop() selectedRowCount: number; + + /** @internal */ + @Prop() selectedRowCountLocalized: string; + + @Watch("selectedRowCount") + @Watch("selectedRowCountLocalized") + onSelectedChange(): void { + this.updateScreenReaderText(); + } + + /** @internal */ + @Prop() selectionCell = false; + + /** @internal */ + @Prop() selectionMode: SelectionMode; + + /** @internal */ + @Prop() bodyRowCount: number; + + /** + * Made into a prop for testing purposes only + * + * @internal + */ + // eslint-disable-next-line @stencil-community/strict-mutable -- updated by t9n module + @Prop({ mutable: true }) messages: TableHeaderMessages; + + /** + * Use this property to override individual strings used by the component. + */ + // eslint-disable-next-line @stencil-community/strict-mutable -- updated by t9n module + @Prop({ mutable: true }) messageOverrides: Partial; + + @Watch("messageOverrides") + onMessagesChange(): void { + /* wired up by t9n util */ + } + + // -------------------------------------------------------------------------- + // + // Lifecycle + // + // -------------------------------------------------------------------------- + + async componentWillLoad(): Promise { + setUpLoadableComponent(this); + await setUpMessages(this); + this.updateScreenReaderText(); + } + + componentDidLoad(): void { + setComponentLoaded(this); + } + + connectedCallback(): void { + connectLocalized(this); + connectMessages(this); + } + + disconnectedCallback(): void { + disconnectLocalized(this); + disconnectMessages(this); + } + + // -------------------------------------------------------------------------- + // + // Private Properties + // + // -------------------------------------------------------------------------- + @Element() el: HTMLCalciteTableHeaderElement; + + @State() defaultMessages: TableHeaderMessages; + + @State() screenReaderText = ""; + + @State() effectiveLocale = ""; + + @Watch("effectiveLocale") + effectiveLocaleChange(): void { + updateMessages(this, this.effectiveLocale); + } + + private containerEl: HTMLTableCellElement; + + // -------------------------------------------------------------------------- + // + // Public Methods + // + // -------------------------------------------------------------------------- + + /** Sets focus on the component. */ + @Method() + async setFocus(): Promise { + await componentFocusable(this); + this.containerEl.focus(); + } + + // -------------------------------------------------------------------------- + // + // Private Methods + // + // -------------------------------------------------------------------------- + + private updateScreenReaderText(): void { + let text = ""; + const sharedText = `${this.selectedRowCountLocalized} ${this.messages?.selected}`; + if (this.numberCell) { + text = this.messages?.rowNumber; + } else if (this.selectionMode === "single") { + text = `${this.messages?.selectionColumn}. ${sharedText}`; + } else if (this.bodyRowCount === this.selectedRowCount) { + text = `${this.messages?.selectionColumn}. ${this.messages?.all} ${sharedText} ${this.messages?.keyboardDeselectAll}`; + } else { + text = `${this.messages?.selectionColumn}. ${sharedText} ${this.messages?.keyboardSelectAll}`; + } + this.screenReaderText = text; + } + + //-------------------------------------------------------------------------- + // + // Render Methods + // + //-------------------------------------------------------------------------- + + render(): VNode { + const scope = this.rowSpan + ? "rowgroup" + : this.colSpan + ? "colgroup" + : this.parentRowType === "body" + ? "row" + : "col"; + + const allSelected = this.selectedRowCount === this.bodyRowCount; + const selectionIcon = allSelected ? "check-square-f" : "check-square"; + + return ( + + (this.containerEl = el)} + > + {this.heading &&
{this.heading}
} + {this.description &&
{this.description}
} + {this.selectionCell && this.selectionMode === "multiple" && ( + + )} + {(this.selectionCell || this.numberCell) && ( + + {this.screenReaderText} + + )} + +
+ ); + } +} diff --git a/packages/calcite-components/src/components/table-row/readme.md b/packages/calcite-components/src/components/table-row/readme.md new file mode 100644 index 00000000000..48216d72890 --- /dev/null +++ b/packages/calcite-components/src/components/table-row/readme.md @@ -0,0 +1,53 @@ +# calcite-table-row + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ---------- | ---------- | ---------------------------------------------------------------------------------------- | ------------------- | ------- | +| `disabled` | `disabled` | When `true`, interaction is prevented and the component is displayed with lower opacity. | `boolean` | `false` | +| `scale` | `scale` | Specifies the size of the component. | `"l" \| "m" \| "s"` | `"m"` | +| `selected` | `selected` | Is the component selected. | `boolean` | `false` | + +## Events + +| Event | Description | Type | +| ----------------------- | ------------------------------------------------------- | ------------------- | +| `calciteTableRowSelect` | Fires when the selected state of the component changes. | `CustomEvent` | + +## Slots + +| Slot | Description | +| ---- | -------------------------------------------------------------------------- | +| | A slot for adding `calcite-table-cell` or `calcite-table-header` elements. | + +## CSS Custom Properties + +| Name | Description | +| ---------------------------------- | ------------------------------------------------ | +| `--calcite-table-row-background` | Specifies the background color of the component. | +| `--calcite-table-row-border-color` | Specifies the border color of the component. | + +## Dependencies + +### Depends on + +- [calcite-icon](../icon) +- [calcite-table-header](../table-header) +- [calcite-table-cell](../table-cell) + +### Graph + +```mermaid +graph TD; + calcite-table-row --> calcite-icon + calcite-table-row --> calcite-table-header + calcite-table-row --> calcite-table-cell + calcite-table-header --> calcite-icon + style calcite-table-row fill:#f9f,stroke:#333,stroke-width:4px +``` + +--- + +_Built with [StencilJS](https://stenciljs.com/)_ diff --git a/packages/calcite-components/src/components/table-row/table-row.scss b/packages/calcite-components/src/components/table-row/table-row.scss new file mode 100644 index 00000000000..4b2454beb89 --- /dev/null +++ b/packages/calcite-components/src/components/table-row/table-row.scss @@ -0,0 +1,27 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-table-row-background: Specifies the background color of the component. + * @prop --calcite-table-row-border-color: Specifies the border color of the component. + */ + +:host { + --calcite-internal-table-row-background: var(--calcite-table-row-background, var(--calcite-ui-foreground-1)); + --calcite-internal-table-row-border-color: var(--calcite-table-row-border-color, transparent); + @apply contents; +} + +@include base-component(); + +@include disabled() { + tr { + @apply opacity-disabled pointer-events-none; + } +} + +tr { + border-block-end: 1px solid var(--calcite-internal-table-row-border-color); + background-color: var(--calcite-internal-table-row-background); +} diff --git a/packages/calcite-components/src/components/table-row/table-row.tsx b/packages/calcite-components/src/components/table-row/table-row.tsx new file mode 100644 index 00000000000..79be55a2f7b --- /dev/null +++ b/packages/calcite-components/src/components/table-row/table-row.tsx @@ -0,0 +1,393 @@ +import { + Component, + Element, + Event, + EventEmitter, + h, + Host, + Listen, + Prop, + State, + VNode, + Watch, +} from "@stencil/core"; +import { LocalizedComponent } from "../../utils/locale"; +import { Scale, SelectionMode } from "../interfaces"; +import { focusElementInGroup, FocusElementInGroupDestination } from "../../utils/dom"; +import { RowType, TableRowFocusEvent } from "../table/interfaces"; +import { isActivationKey } from "../../utils/key"; +import { + connectInteractive, + disconnectInteractive, + InteractiveComponent, + updateHostInteraction, +} from "../../utils/interactive"; + +/** + * @slot - A slot for adding `calcite-table-cell` or `calcite-table-header` elements. + */ + +@Component({ + tag: "calcite-table-row", + styleUrl: "table-row.scss", + shadow: true, +}) +export class TableRow implements InteractiveComponent, LocalizedComponent { + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** When `true`, interaction is prevented and the component is displayed with lower opacity. */ + @Prop({ reflect: true }) disabled = false; + + /** When `true`, the component is selected. */ + @Prop({ reflect: true }) selected = false; + + /** @internal */ + @Prop({ mutable: true }) cellCount: number; + + /** @internal */ + @Prop() rowType: RowType; + + /** @internal */ + @Prop() numbered = false; + + /** @internal */ + @Prop() positionSection: number; + + /** @internal */ + @Prop() positionSectionLocalized: string; + + /** @internal */ + @Prop() positionAll: number; + + /** @internal */ + @Prop() readCellContentsToAT: boolean; + + /** @internal */ + @Prop() scale: Scale; + + /** @internal */ + @Prop() selectionMode: Extract<"multiple" | "single" | "none", SelectionMode> = "none"; + + /** @internal */ + @Prop() selectedRowCount: number; + + /** @internal */ + @Prop() selectedRowCountLocalized: string; + + /** @internal */ + @Prop() bodyRowCount: number; + + @Watch("bodyRowCount") + @Watch("scale") + @Watch("selected") + @Watch("selectedRowCount") + handleCellChanges(): void { + if (this.tableRowEl && this.rowCells.length > 0) { + this.updateCells(); + } + } + + @Watch("numbered") + @Watch("selectionMode") + handleDelayedCellChanges(): void { + if (this.tableRowEl && this.rowCells.length > 0) { + requestAnimationFrame(() => this.updateCells()); + } + } + + //-------------------------------------------------------------------------- + // + // Lifecycle + // + //-------------------------------------------------------------------------- + + componentDidLoad(): void { + if (this.tableRowEl && this.rowCells.length > 0) { + this.updateCells(); + } + } + + connectedCallback(): void { + connectInteractive(this); + } + + componentDidRender(): void { + updateHostInteraction(this); + } + + disconnectedCallback(): void { + disconnectInteractive(this); + } + + //-------------------------------------------------------------------------- + // + // Private Properties + // + //-------------------------------------------------------------------------- + + @Element() el: HTMLCalciteTableRowElement; + + private rowCells: (HTMLCalciteTableCellElement | HTMLCalciteTableHeaderElement)[] = []; + + private tableRowEl: HTMLTableRowElement; + + private tableRowSlotEl: HTMLSlotElement; + + @State() effectiveLocale = ""; + + // -------------------------------------------------------------------------- + // + // Events + // + // -------------------------------------------------------------------------- + + /** + * Fires when the selected state of the component changes. + */ + @Event({ cancelable: false }) calciteTableRowSelect: EventEmitter; + + /** @internal */ + @Event({ cancelable: false }) + calciteInternalTableRowFocusRequest: EventEmitter; + + //-------------------------------------------------------------------------- + // + // Event Listeners + // + //-------------------------------------------------------------------------- + + @Listen("calciteInternalTableRowFocusChange", { target: "document" }) + calciteInternalTableRowFocusChangeHandler(event: CustomEvent): void { + if ((event.target as Element).contains(this.el)) { + const position = event.detail.cellPosition; + const rowPosition = event.detail.rowPosition; + const destination = event.detail.destination; + const lastCell = event.detail.lastCell; + + if (rowPosition === this.positionAll) { + if (this.disabled) { + const deflectDirection = + destination === "last" ? "previous" : destination === "first" ? "next" : destination; + this.emitTableRowFocusRequest(position, this.positionAll, deflectDirection); + return; + } + const cellPosition = lastCell + ? this.rowCells[this.rowCells.length - 1] + : this.rowCells?.find((_, index) => index + 1 === position); + + if (cellPosition) { + cellPosition.setFocus(); + } + } + } + } + + //-------------------------------------------------------------------------- + // + // Private Methods + // + //-------------------------------------------------------------------------- + + private keyDownHandler(event: KeyboardEvent): void { + const el = event.target as HTMLCalciteTableCellElement | HTMLCalciteTableHeaderElement; + const key = event.key; + const isControl = event.ctrlKey; + const cells = this.rowCells; + if (el.matches("calcite-table-cell") || el.matches("calcite-table-header")) { + switch (key) { + case "ArrowUp": + this.emitTableRowFocusRequest(el.positionInRow, this.positionAll, "previous"); + event.preventDefault(); + break; + case "ArrowDown": + this.emitTableRowFocusRequest(el.positionInRow, this.positionAll, "next"); + event.preventDefault(); + break; + case "PageUp": + this.emitTableRowFocusRequest(el.positionInRow, this.positionAll, "first"); + event.preventDefault(); + break; + case "PageDown": + this.emitTableRowFocusRequest(el.positionInRow, this.positionAll, "last"); + event.preventDefault(); + break; + case "ArrowLeft": + focusElementInGroup(cells, el, "previous", false); + event.preventDefault(); + break; + case "ArrowRight": + focusElementInGroup(cells, el, "next", false); + event.preventDefault(); + break; + case "Home": + if (isControl) { + this.emitTableRowFocusRequest(1, this.positionAll, "first"); + event.preventDefault(); + } else { + focusElementInGroup(cells, el, "first", false); + event.preventDefault(); + } + break; + case "End": + if (isControl) { + this.emitTableRowFocusRequest(this.rowCells?.length, this.positionAll, "last", true); + event.preventDefault(); + } else { + focusElementInGroup(cells, el, "last", false); + event.preventDefault(); + } + break; + } + } + } + + private emitTableRowFocusRequest = ( + cellPosition: number, + rowPosition: number, + destination: FocusElementInGroupDestination, + lastCell?: boolean + ): void => { + this.calciteInternalTableRowFocusRequest.emit({ + cellPosition, + rowPosition, + destination, + lastCell, + }); + }; + + private updateCells = (): void => { + const slottedCells = this.tableRowSlotEl + ?.assignedElements({ flatten: true }) + ?.filter( + (el: HTMLCalciteTableCellElement | HTMLCalciteTableHeaderElement) => + el.matches("calcite-table-cell") || el.matches("calcite-table-header") + ); + + const renderedCells = Array.from( + this.tableRowEl?.querySelectorAll("calcite-table-header, calcite-table-cell") + )?.filter( + (el: HTMLCalciteTableCellElement | HTMLCalciteTableHeaderElement) => + el.numberCell || el.selectionCell + ); + + const cells = renderedCells ? renderedCells.concat(slottedCells) : slottedCells; + + if (cells.length > 0) { + cells?.forEach((cell: HTMLCalciteTableCellElement | HTMLCalciteTableHeaderElement, index) => { + cell.positionInRow = index + 1; + cell.parentRowType = this.rowType; + cell.scale = this.scale; + + if (cell.nodeName === "CALCITE-TABLE-CELL") { + (cell as HTMLCalciteTableCellElement).readCellContentsToAT = this.readCellContentsToAT; + (cell as HTMLCalciteTableCellElement).disabled = this.disabled; + (cell as HTMLCalciteTableCellElement).parentRowIsSelected = this.selected; + } + }); + } + + this.rowCells = + (cells as (HTMLCalciteTableCellElement | HTMLCalciteTableHeaderElement)[]) || []; + this.cellCount = cells?.length; + }; + + private handleSelectionOfRow = (): void => { + this.calciteTableRowSelect.emit(); + }; + + private handleKeyboardSelection = (event: KeyboardEvent): void => { + if (isActivationKey(event.key)) { + if (event.key === " ") { + event.preventDefault(); + } + this.handleSelectionOfRow(); + } + }; + + //-------------------------------------------------------------------------- + // + // Render Methods + // + //-------------------------------------------------------------------------- + + renderSelectionIcon(): VNode { + const icon = + this.selectionMode === "multiple" && this.selected + ? "check-square-f" + : this.selectionMode === "multiple" + ? "square" + : this.selected + ? "circle-f" + : "circle"; + + return ; + } + + renderSelectableCell(): VNode { + return this.rowType === "head" ? ( + + ) : this.rowType === "body" ? ( + + {this.renderSelectionIcon()} + + ) : ( + + ); + } + + renderNumberedCell(): VNode { + return this.rowType === "head" ? ( + + ) : this.rowType === "body" ? ( + + {this.positionSectionLocalized} + + ) : ( + + ); + } + + render(): VNode { + return ( + + this.keyDownHandler(event)} + // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530) + ref={(el) => (this.tableRowEl = el)} + > + {this.numbered && this.renderNumberedCell()} + {this.selectionMode !== "none" && this.renderSelectableCell()} + (this.tableRowSlotEl = el as HTMLSlotElement)} + /> + + + ); + } +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_ar.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ar.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ar.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_bg.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_bg.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_bg.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_bs.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_bs.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_bs.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_ca.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ca.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ca.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_cs.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_cs.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_cs.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_da.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_da.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_da.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_de.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_de.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_de.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_el.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_el.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_el.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_en.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_en.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_en.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_es.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_es.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_es.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_et.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_et.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_et.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_fi.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_fi.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_fi.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_fr.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_fr.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_fr.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_he.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_he.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_he.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_hr.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_hr.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_hr.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_hu.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_hu.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_hu.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_id.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_id.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_id.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_it.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_it.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_it.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_ja.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ja.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ja.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_ko.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ko.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ko.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_lt.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_lt.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_lt.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_lv.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_lv.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_lv.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_nl.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_nl.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_nl.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_no.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_no.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_no.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_pl.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_pl.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_pl.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_pt-BR.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_pt-BR.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_pt-BR.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_pt-PT.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_pt-PT.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_pt-PT.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_ro.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ro.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ro.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_ru.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ru.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_ru.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_sk.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_sk.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_sk.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_sl.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_sl.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_sl.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_sr.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_sr.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_sr.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_sv.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_sv.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_sv.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_th.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_th.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_th.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_tr.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_tr.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_tr.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_uk.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_uk.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_uk.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_vi.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_vi.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_vi.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-CN.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-CN.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-CN.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-HK.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-HK.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-HK.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-TW.json b/packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-TW.json new file mode 100644 index 00000000000..76931e75ab2 --- /dev/null +++ b/packages/calcite-components/src/components/table/assets/table/t9n/messages_zh-TW.json @@ -0,0 +1,7 @@ +{ + "clear": "Clear", + "hiddenSelected": "Rows selected but out of view", + "page": "Page", + "row": "Row", + "selected": "Selected" +} diff --git a/packages/calcite-components/src/components/table/interfaces.ts b/packages/calcite-components/src/components/table/interfaces.ts new file mode 100644 index 00000000000..90f24e94c7f --- /dev/null +++ b/packages/calcite-components/src/components/table/interfaces.ts @@ -0,0 +1,14 @@ +import { FocusElementInGroupDestination } from "../../utils/dom"; + +export interface TableRowFocusEvent { + cellPosition: number; + rowPosition: number; + destination: FocusElementInGroupDestination; + lastCell: boolean; +} + +export type RowType = "head" | "body" | "foot"; + +export type TableAppearance = "bordered" | "simple" | "bordered-zebra" | "simple-zebra"; + +export type TableLayout = "auto" | "fixed"; diff --git a/packages/calcite-components/src/components/table/readme.md b/packages/calcite-components/src/components/table/readme.md new file mode 100644 index 00000000000..06294a8a158 --- /dev/null +++ b/packages/calcite-components/src/components/table/readme.md @@ -0,0 +1,50 @@ +# calcite-table + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ---------------------- | ---------------- | ------------------------------------------------------------- | -------------------------------------------------------------- | ----------- | +| `appearance` | `appearance` | Specifies the appearance of the component. | `"bordered" \| "bordered-zebra" \| "simple" \| "simple-zebra"` | `"simple"` | +| `caption` _(required)_ | `caption` | Specifies an accessible title for the component. | `string` | `undefined` | +| `layout` | `layout` | Specifies the layout of the component. | `"auto" \| "fixed"` | `"auto"` | +| `numbered` | `numbered` | When `true`, displays the position of the row in numeric form | `boolean` | `false` | +| `scale` | `scale` | Specifies the size of the component. | `"l" \| "m" \| "s"` | `"m"` | +| `selectedItems` | -- | Specifies the component's selected items. | `HTMLCalciteTableRowElement[]` | `[]` | +| `selectionMode` | `selection-mode` | Specifies the selection mode of the component. | `"multiple" \| "none" \| "single"` | `"none"` | + +## Events + +| Event | Description | Type | +| -------------------- | --------------------------------------------- | ------------------- | +| `calciteTableSelect` | Emits when the component's selection changes. | `CustomEvent` | + +## Slots + +| Slot | Description | +| ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| | A slot for adding `calcite-table-row` or nested `calcite-table` elements. Content placed here will be rendered in a `table-body` tag. | +| `"selection-actions"` | A slot for adding a `calcite-action` or other element to display when `selectionMode !== "none"` and `calcite-table-row` are selected. | +| `"selection-actions"` | A slot for adding a `calcite-action` or other element to display when `selectionMode !== "none"` and `calcite-table-row` are selected. | +| `"table-foot- A slot for adding `calcite-table-row`and nested`calcite-table-header` elements."` | | +| `"table-head- A slot for adding `calcite-table-row`and nested`calcite-table-header` elements."` | | + +## Dependencies + +### Depends on + +- [calcite-chip](../chip) + +### Graph + +```mermaid +graph TD; + calcite-table --> calcite-chip + calcite-chip --> calcite-icon + style calcite-table fill:#f9f,stroke:#333,stroke-width:4px +``` + +--- + +_Built with [StencilJS](https://stenciljs.com/)_ diff --git a/packages/calcite-components/src/components/table/resources.ts b/packages/calcite-components/src/components/table/resources.ts new file mode 100644 index 00000000000..93646dda10f --- /dev/null +++ b/packages/calcite-components/src/components/table/resources.ts @@ -0,0 +1,17 @@ +export const CSS = { + bordered: "bordered", + zebra: "zebra", + selectionArea: "selection-area", + paginationArea: "pagination-area", + container: "container", + tableContainer: "table-container", + tableFixed: "table--fixed", + assistiveText: "assistive-text", + selectionActions: "selection-actions", +}; + +export const SLOTS = { + selectionActions: "selection-actions", + tableHeader: "table-header", + tableFooter: "table-footer", +}; diff --git a/packages/calcite-components/src/components/table/table.e2e.ts b/packages/calcite-components/src/components/table/table.e2e.ts new file mode 100644 index 00000000000..407f0868e34 --- /dev/null +++ b/packages/calcite-components/src/components/table/table.e2e.ts @@ -0,0 +1,2427 @@ +import { E2EPage, newE2EPage } from "@stencil/core/testing"; +import { html } from "../../../support/formatting"; +import { accessible, renders, hidden, defaults, reflects } from "../../tests/commonTests"; +import { GlobalTestProps, getFocusedElementProp } from "../../tests/utils"; +import { CSS } from "../table-header/resources"; +import { CSS as CELL_CSS } from "../table-cell/resources"; +import { SLOTS } from "../table/resources"; + +describe("calcite-table", () => { + describe("renders", () => { + renders( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + `, + { display: "table" } + ); + }); + + describe("defaults", () => { + defaults("calcite-table", [ + { + propertyName: "bordered", + defaultValue: false, + }, + { + propertyName: "groupSeparator", + defaultValue: false, + }, + { + propertyName: "layout", + defaultValue: "auto", + }, + { + propertyName: "numbered", + defaultValue: false, + }, + { + propertyName: "pageSize", + defaultValue: 0, + }, + { + propertyName: "scale", + defaultValue: "m", + }, + { + propertyName: "selectionMode", + defaultValue: "none", + }, + { + propertyName: "zebra", + defaultValue: false, + }, + ]); + }); + + describe("reflects", () => { + reflects("calcite-table", [ + { + propertyName: "layout", + value: "auto", + }, + { + propertyName: "scale", + value: "m", + }, + { + propertyName: "selectionMode", + value: "none", + }, + ]); + }); + + describe("hidden", () => { + hidden("calcite-table"); + }); + + describe("accessible", () => { + describe("is accessible simple", () => { + accessible( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + }); + + describe("is accessible with selection mode multiple", () => { + accessible( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + }); + + describe("is accessible with selection mode multiple selected at load", () => { + accessible( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + }); + + describe("is accessible with selection mode single", () => { + accessible( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + }); + + describe("is accessible with numbered", () => { + accessible( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + }); + + describe("is accessible with numbered and selection", () => { + accessible( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + }); + + describe("is accessible with pagination", () => { + accessible( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + }); + + describe("is accessible with pagination and selection mode", () => { + accessible( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + }); + }); +}); + +describe("selection modes", () => { + it("selection mode single allows one or no rows to be selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + const rowSelectSpy1 = await row1.spyOnEvent("calciteTableRowSelect"); + const rowSelectSpy2 = await row2.spyOnEvent("calciteTableRowSelect"); + const rowSelectSpy3 = await row3.spyOnEvent("calciteTableRowSelect"); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(rowSelectSpy1).toHaveReceivedEventTimes(0); + expect(rowSelectSpy2).toHaveReceivedEventTimes(0); + expect(rowSelectSpy3).toHaveReceivedEventTimes(0); + + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row3.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-1"); + const cell = row.shadowRoot.querySelector("calcite-table-cell:first-child"); + + cell.click(); + }); + + await page.waitForChanges(); + expect(await tableSelectSpy).toHaveReceivedEventTimes(1); + expect(await rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(await rowSelectSpy2).toHaveReceivedEventTimes(0); + expect(await rowSelectSpy3).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row1.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-2"); + const cell = row.shadowRoot.querySelector("calcite-table-cell:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(2); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row2.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-3"); + const cell = row.shadowRoot.querySelector("calcite-table-cell:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(3); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row3.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-3"); + const cell = row.shadowRoot.querySelector("calcite-table-cell:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(4); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(2); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + + expect(await element.getProperty("selectedItems")).toEqual([]); + await assertSelectedItems(page, { expectedItemIds: [] }); + }); + + it("selection mode multiple allows one, multiple, or no rows to be selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + const rowSelectSpy1 = await row1.spyOnEvent("calciteTableRowSelect"); + const rowSelectSpy2 = await row2.spyOnEvent("calciteTableRowSelect"); + const rowSelectSpy3 = await row3.spyOnEvent("calciteTableRowSelect"); + + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(rowSelectSpy1).toHaveReceivedEventTimes(0); + expect(rowSelectSpy2).toHaveReceivedEventTimes(0); + expect(rowSelectSpy3).toHaveReceivedEventTimes(0); + + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await assertSelectedItems(page, { expectedItemIds: [row2.id, row3.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-1"); + const cell = row.shadowRoot.querySelector("calcite-table-cell:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(await tableSelectSpy).toHaveReceivedEventTimes(1); + expect(await rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(await rowSelectSpy2).toHaveReceivedEventTimes(0); + expect(await rowSelectSpy3).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(3); + await assertSelectedItems(page, { expectedItemIds: [row1.id, row2.id, row3.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-2"); + const cell = row.shadowRoot.querySelector("calcite-table-cell:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(2); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await assertSelectedItems(page, { expectedItemIds: [row1.id, row3.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-3"); + const cell = row.shadowRoot.querySelector("calcite-table-cell:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(3); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row1.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-1"); + const cell = row.shadowRoot.querySelector("calcite-table-cell:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(4); + expect(rowSelectSpy1).toHaveReceivedEventTimes(2); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + + expect(await element.getProperty("selectedItems")).toEqual([]); + await assertSelectedItems(page, { expectedItemIds: [] }); + }); + + it("selection mode single allows one or no rows to be selected with keyboard", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const selectionCell1 = await page.find("#row-1 >>> calcite-table-cell:first-child"); + const selectionCell2 = await page.find("#row-2 >>> calcite-table-cell:first-child"); + const selectionCell3 = await page.find("#row-3 >>> calcite-table-cell:first-child"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + const rowSelectSpy1 = await row1.spyOnEvent("calciteTableRowSelect"); + const rowSelectSpy2 = await row2.spyOnEvent("calciteTableRowSelect"); + const rowSelectSpy3 = await row3.spyOnEvent("calciteTableRowSelect"); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(rowSelectSpy1).toHaveReceivedEventTimes(0); + expect(rowSelectSpy2).toHaveReceivedEventTimes(0); + expect(rowSelectSpy3).toHaveReceivedEventTimes(0); + + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row3.id] }); + await selectionCell1.callMethod("setFocus"); + await page.waitForChanges(); + + await page.keyboard.press("Space"); + await page.waitForChanges(); + + expect(await tableSelectSpy).toHaveReceivedEventTimes(1); + expect(await rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(await rowSelectSpy2).toHaveReceivedEventTimes(0); + expect(await rowSelectSpy3).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row1.id] }); + + await selectionCell2.callMethod("setFocus"); + await page.waitForChanges(); + await page.keyboard.press("Enter"); + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(2); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row2.id] }); + + await selectionCell3.callMethod("setFocus"); + await page.waitForChanges(); + await page.keyboard.press("Enter"); + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(3); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row3.id] }); + + await selectionCell3.callMethod("setFocus"); + + await page.waitForChanges(); + await page.keyboard.press("Space"); + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(4); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(2); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + + expect(await element.getProperty("selectedItems")).toEqual([]); + await assertSelectedItems(page, { expectedItemIds: [] }); + }); + + it("selection mode multiple allows one, multiple, or no rows to be selected with keyboard", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + const selectionCell1 = await page.find("#row-1 >>> calcite-table-cell:first-child"); + const selectionCell2 = await page.find("#row-2 >>> calcite-table-cell:first-child"); + const selectionCell3 = await page.find("#row-3 >>> calcite-table-cell:first-child"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + const rowSelectSpy1 = await row1.spyOnEvent("calciteTableRowSelect"); + const rowSelectSpy2 = await row2.spyOnEvent("calciteTableRowSelect"); + const rowSelectSpy3 = await row3.spyOnEvent("calciteTableRowSelect"); + + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(rowSelectSpy1).toHaveReceivedEventTimes(0); + expect(rowSelectSpy2).toHaveReceivedEventTimes(0); + expect(rowSelectSpy3).toHaveReceivedEventTimes(0); + + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await assertSelectedItems(page, { expectedItemIds: [row2.id, row3.id] }); + + await selectionCell1.callMethod("setFocus"); + + await page.waitForChanges(); + await page.keyboard.press("Enter"); + await page.waitForChanges(); + expect(await tableSelectSpy).toHaveReceivedEventTimes(1); + expect(await rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(await rowSelectSpy2).toHaveReceivedEventTimes(0); + expect(await rowSelectSpy3).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(3); + await assertSelectedItems(page, { expectedItemIds: [row1.id, row2.id, row3.id] }); + + await selectionCell2.callMethod("setFocus"); + + await page.waitForChanges(); + await page.keyboard.press("Space"); + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(2); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await assertSelectedItems(page, { expectedItemIds: [row1.id, row3.id] }); + + await selectionCell3.callMethod("setFocus"); + + await page.waitForChanges(); + await page.keyboard.press("Enter"); + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(3); + expect(rowSelectSpy1).toHaveReceivedEventTimes(1); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row1.id] }); + + await selectionCell1.callMethod("setFocus"); + + await page.waitForChanges(); + await page.keyboard.press("Space"); + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(4); + expect(rowSelectSpy1).toHaveReceivedEventTimes(2); + expect(rowSelectSpy2).toHaveReceivedEventTimes(1); + expect(rowSelectSpy3).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + + expect(await element.getProperty("selectedItems")).toEqual([]); + await assertSelectedItems(page, { expectedItemIds: [] }); + }); + it("correctly has no selected items after user clears selection via clear button", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await assertSelectedItems(page, { expectedItemIds: [row2.id, row3.id] }); + + await page.$eval("calcite-table", () => { + const table = document.querySelector("calcite-table"); + const button = table.shadowRoot.querySelector("calcite-button"); + button?.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await assertSelectedItems(page, { expectedItemIds: [] }); + }); + + it("correctly has all items selected after user uses select all cell while none selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await assertSelectedItems(page, { expectedItemIds: [] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-head"); + const cell = row.shadowRoot.querySelector("calcite-table-header:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(3); + await assertSelectedItems(page, { expectedItemIds: [row1.id, row2.id, row3.id] }); + }); + + it("correctly has all items selected after user uses select all cell while none selected and multiple pages", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await assertSelectedItems(page, { expectedItemIds: [] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-head"); + const cell = row.shadowRoot.querySelector("calcite-table-header:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(3); + await assertSelectedItems(page, { expectedItemIds: [row1.id, row2.id, row3.id] }); + }); + + it("correctly has all items selected after user uses select all cell while some selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row2.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-head"); + const cell = row.shadowRoot.querySelector("calcite-table-header:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(await tableSelectSpy).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(3); + await assertSelectedItems(page, { expectedItemIds: [row1.id, row2.id, row3.id] }); + }); + + it("correctly has no items selected after user uses select none cell while all selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(3); + await assertSelectedItems(page, { expectedItemIds: [row1.id, row2.id, row3.id] }); + + await page.$eval("calcite-table", () => { + const row = document.getElementById("row-head"); + const cell = row.shadowRoot.querySelector("calcite-table-header:first-child"); + cell.click(); + }); + + await page.waitForChanges(); + expect(await tableSelectSpy).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await assertSelectedItems(page, { expectedItemIds: [] }); + }); + + it("correctly maintains selected items if they are paginated out of view", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + const tablePaginateSpy = await element.spyOnEvent("calciteTablePageChange"); + + await page.waitForChanges(); + + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(tablePaginateSpy).toHaveReceivedEventTimes(0); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row1.id] }); + + await page.$eval("calcite-table", () => { + const table = document.querySelector("calcite-table"); + const pagination = table.shadowRoot.querySelector("calcite-pagination"); + const button = pagination.shadowRoot.querySelector("button.page:nth-of-type(2)"); + + button?.click(); + }); + + await page.waitForChanges(); + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(tablePaginateSpy).toHaveReceivedEventTimes(1); + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [row1.id] }); + }); +}); + +describe("pagination event", () => { + it("correctly emits pagination event", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await assertSelectedItems.setUpEvents(page); + const element = await page.find("calcite-table"); + const tablePaginateSpy = await element.spyOnEvent("calciteTablePageChange"); + await page.waitForChanges(); + + expect(tablePaginateSpy).toHaveReceivedEventTimes(0); + + await page.$eval("calcite-table", () => { + const table = document.querySelector("calcite-table"); + const pagination = table.shadowRoot.querySelector("calcite-pagination"); + const button = pagination.shadowRoot.querySelector("button.page:nth-of-type(2)"); + + button?.click(); + }); + + await page.waitForChanges(); + expect(tablePaginateSpy).toHaveReceivedEventTimes(1); + + await page.$eval("calcite-table", () => { + const table = document.querySelector("calcite-table"); + const pagination = table.shadowRoot.querySelector("calcite-pagination"); + const button = pagination.shadowRoot.querySelector("button.page:nth-of-type(3)"); + + button?.click(); + }); + + await page.waitForChanges(); + expect(tablePaginateSpy).toHaveReceivedEventTimes(2); + + await page.$eval("calcite-table", () => { + const table = document.querySelector("calcite-table"); + const pagination = table.shadowRoot.querySelector("calcite-pagination"); + const button = pagination.shadowRoot.querySelector("button.page:nth-of-type(4)"); + + button?.click(); + }); + + await page.waitForChanges(); + expect(tablePaginateSpy).toHaveReceivedEventTimes(3); + }); +}); + +describe("keyboard navigation", () => { + it("navigates correctly when no pagination or selection present", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + page.keyboard.press("ControlLeft"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3b"); + page.keyboard.press("ControlLeft"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3b"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + }); + + it("navigates correctly when pagination present and first page displayed", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2b"); + page.keyboard.press("ControlLeft"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2b"); + page.keyboard.press("ControlLeft"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + }); + + it("navigates correctly when pagination present, and navigation to two other pages occurs", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2b"); + page.keyboard.press("ControlLeft"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2b"); + page.keyboard.press("ControlLeft"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + + await page.$eval("calcite-table", () => { + const table = document.querySelector("calcite-table"); + const headerCell = document.getElementById("head-1a"); + + const pagination = table.shadowRoot.querySelector("calcite-pagination"); + const button = pagination.shadowRoot.querySelector("button.page:nth-of-type(3)"); + button?.click(); + (headerCell as HTMLCalciteTableHeaderElement).setFocus(); + }); + + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4b"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4b"); + + await page.$eval("calcite-table", () => { + const table = document.querySelector("calcite-table"); + const headerCell = document.getElementById("head-1a"); + + const pagination = table.shadowRoot.querySelector("calcite-pagination"); + const button = pagination.shadowRoot.querySelector("button.page:nth-of-type(4)"); + + button?.click(); + (headerCell as HTMLCalciteTableHeaderElement).setFocus(); + }); + + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-5b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-5a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-5a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-5a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-5a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-5b"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-5b"); + }); + + it("navigates correctly skipping disabled rows", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1b"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4b"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1b"); + }); + + it("navigates correctly skipping disabled rows when disabled rows in last body position", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + ` + ); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1b"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3b"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3b"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3b"); + }); + + it("navigates correctly when multiple header and multiple footer rows", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + foot + foot + + + foot + foot + + + >;` + ); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2d"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2b"); + await page.keyboard.press("PageUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2b"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1b"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2b"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + }); + + it("navigates correctly when multiple header and multiple footer rows, pagination present, and navigation to other page occurs", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + foot + foot + + + foot + foot + + + >;` + ); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2d"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-1a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2a"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2b"); + + await page.$eval("calcite-table", () => { + const table = document.querySelector("calcite-table"); + const headerCell = document.getElementById("head-1a"); + + const pagination = table.shadowRoot.querySelector("calcite-pagination"); + const button = pagination.shadowRoot.querySelector("button.page:nth-of-type(3)"); + + button?.click(); + (headerCell as HTMLCalciteTableHeaderElement).setFocus(); + }); + + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2d"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2b"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1a"); + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-4a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2a"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-2b"); + page.keyboard.press("ControlRight"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-2a"); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + }); + + it("navigates correctly when selection column present", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + + ` + ); + + const rowHead = await page.find("#row-head"); + const rowFoot = await page.find("#row-foot"); + const row3 = await page.find("#row-3"); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.selectionCell, "1": CSS.multipleSelectionCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.selectionCell, "1": CSS.multipleSelectionCell }); + + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowFoot.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CELL_CSS.footerCell, "1": CSS.selectionCell }); + + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row3.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1b"); + + page.keyboard.press("ControlLeft"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.selectionCell, "1": CSS.multipleSelectionCell }); + }); + + it("navigates correctly when number column present", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + + ` + ); + + const rowHead = await page.find("#row-head"); + const rowFoot = await page.find("#row-foot"); + const row3 = await page.find("#row-3"); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowFoot.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CELL_CSS.footerCell, "1": CSS.numberCell }); + + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row3.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1b"); + + page.keyboard.press("ControlLeft"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + }); + + it("navigates correctly when number and selection column present numbered", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + + ` + ); + + const rowHead = await page.find("#row-head"); + const rowFoot = await page.find("#row-foot"); + const row3 = await page.find("#row-3"); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowFoot.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CELL_CSS.footerCell, "1": CSS.selectionCell }); + + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row3.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row3.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-3a"); + + page.keyboard.press("ControlRight"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + }); + + it("navigates correctly when pagination present and selection and number and first page displayed", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + + ` + ); + + const rowHead = await page.find("#row-head"); + const rowFoot = await page.find("#row-foot"); + const row2 = await page.find("#row-2"); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.selectionCell, "1": CSS.multipleSelectionCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.selectionCell, "1": CSS.multipleSelectionCell }); + + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowFoot.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CELL_CSS.footerCell, "1": CSS.selectionCell }); + + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row2.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("cell-2a"); + + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1b"); + + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowFoot.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CELL_CSS.footerCell, "1": CSS.numberCell }); + + page.keyboard.press("ControlLeft"); + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + }); + + it("navigates correctly when pagination present, and selection and number and navigation to two other pages occurs", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + + ` + ); + + const rowHead = await page.find("#row-head"); + const rowFoot = await page.find("#row-foot"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + const row4 = await page.find("#row-4"); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1a"); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row1.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row1.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowFoot.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CELL_CSS.footerCell, "1": CSS.numberCell }); + + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row2.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row2.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.selectionCell }); + + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1b"); + + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowFoot.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CELL_CSS.footerCell, "1": CSS.numberCell }); + + page.keyboard.press("ControlRight"); + + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.$eval("calcite-table", () => { + const table = document.querySelector("calcite-table"); + const headerCell = document.getElementById("head-1a"); + + const pagination = table.shadowRoot.querySelector("calcite-pagination"); + const button = pagination.shadowRoot.querySelector("button.page:nth-of-type(3)"); + button?.click(); + (headerCell as HTMLCalciteTableHeaderElement).setFocus(); + }); + + await page.waitForChanges(); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("head-1b"); + + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowHead.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("th").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row3.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.numberCell }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row3.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("PageDown"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowFoot.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CELL_CSS.footerCell, "1": CSS.selectionCell }); + + await page.keyboard.press("ArrowUp"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row4.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.selectionCell }); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect( + await page.$eval(`#${row4.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CSS.numberCell }); + + page.keyboard.press("ControlRight"); + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await getFocusedElementProp(page, "id")).toBe("foot-1b"); + + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect( + await page.$eval(`#${rowFoot.id}`, (el) => el.shadowRoot?.activeElement.shadowRoot?.querySelector("td").classList) + ).toEqual({ "0": CELL_CSS.footerCell, "1": CSS.numberCell }); + }); +}); + +// Borrowed from Dropdown until a generic utility is set up. +interface SelectedItemsAssertionOptions { + /** + * IDs from items to assert selection + */ + expectedItemIds: string[]; +} + +/** + * Test helper for selected calcite-table-row items. Expects items to have IDs to test against. + * + * Note: assertSelectedItems.setUpEvents must be called before using this method + * + * @param page + * @param root0 + * @param root0.expectedItemIds + */ +async function assertSelectedItems(page: E2EPage, { expectedItemIds }: SelectedItemsAssertionOptions): Promise { + await page.waitForTimeout(100); + const selectedItemIds = await page.evaluate(() => { + const table = document.querySelector("calcite-table"); + return table.selectedItems.map((item) => item.id); + }); + + expect(selectedItemIds).toHaveLength(expectedItemIds.length); + + expectedItemIds.forEach((itemId, index) => expect(selectedItemIds[index]).toEqual(itemId)); +} + +type SelectionEventTestWindow = GlobalTestProps<{ eventDetail: Selection }>; + +/** + * Helper to wire up the page to assert on the event detail + * + * @param page + */ +assertSelectedItems.setUpEvents = async (page: E2EPage) => { + await page.evaluate(() => { + document.addEventListener("calciteTableSelect", ({ detail }: CustomEvent) => { + (window as SelectionEventTestWindow).eventDetail = detail; + }); + }); +}; diff --git a/packages/calcite-components/src/components/table/table.scss b/packages/calcite-components/src/components/table/table.scss new file mode 100644 index 00000000000..9e69391bfa0 --- /dev/null +++ b/packages/calcite-components/src/components/table/table.scss @@ -0,0 +1,92 @@ +:host([scale="s"]) { + --calcite-internal-table-cell-padding: 0.25rem; + --calcite-internal-table-cell-font-size: var(--calcite-font-size--2); + --calcite-internal-table-cell-font-size-secondary: var(--calcite-font-size--3); +} +:host([scale="m"]) { + --calcite-internal-table-cell-padding: 0.5rem; + --calcite-internal-table-cell-font-size: var(--calcite-font-size--1); + --calcite-internal-table-cell-font-size-secondary: var(--calcite-font-size--2); +} +:host([scale="l"]) { + --calcite-internal-table-cell-padding: 1rem; + --calcite-internal-table-cell-font-size: var(--calcite-font-size-0); + --calcite-internal-table-cell-font-size-secondary: var(--calcite-font-size--1); +} + +:host { + @apply table; +} + +.container { + max-inline-size: 100vw; + inline-size: 100%; +} + +.table-container { + @apply overflow-x-scroll whitespace-nowrap; +} + +.table-container:not(.bordered) { + border-block-end: 1px solid var(--calcite-ui-border-3); +} + +.assistive-text { + @apply sr-only; +} + +table { + @apply w-full border-collapse; + overflow-x: scroll; + border-block-start: 1px solid var(--calcite-ui-border-3); + border-inline-start: 1px solid var(--calcite-ui-border-3); + border-block-end: 1px solid var(--calcite-ui-border-3); +} + +tbody { + border-block-end: 1px solid var(--calcite-ui-border-3); +} + +.table--fixed { + @apply table-fixed; +} + +.bordered { + ::slotted(calcite-table-row) { + --calcite-table-row-border-color: var(--calcite-ui-border-3); + } +} + +.zebra { + ::slotted(calcite-table-row:nth-child(2n + 1)) { + --calcite-table-row-background: var(--calcite-ui-foreground-2); + } +} +.selection-actions { + @apply flex flex-row; + margin-inline-start: auto; +} + +.selection-area { + @apply flex flex-row items-center; + padding-block: var(--calcite-internal-table-cell-padding); +} + +.selection-area calcite-chip:last-of-type { + @apply me-2; +} + +.selection-area calcite-chip:last-of-type:not(:first-of-type) { + @apply ms-2; +} + +.selection-area calcite-button { + @apply me-4; +} + +.pagination-area { + @apply flex flex-row w-full justify-center; + padding-block: var(--calcite-internal-table-cell-padding); +} + +@include base-component(); diff --git a/packages/calcite-components/src/components/table/table.stories.ts b/packages/calcite-components/src/components/table/table.stories.ts new file mode 100644 index 00000000000..cf2fdd79ab1 --- /dev/null +++ b/packages/calcite-components/src/components/table/table.stories.ts @@ -0,0 +1,912 @@ +import { storyFilters, boolean } from "../../../.storybook/helpers"; +import readme from "./readme.md"; +import { html } from "../../../support/formatting"; +import { modesDarkDefault } from "../../../.storybook/utils"; +import { number, select, text } from "@storybook/addon-knobs"; + +export default { + title: "Components/Table", + parameters: { + notes: readme, + }, + ...storyFilters(), +}; + +export const simple = (): string => + html` + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + `; + +export const simpleZebra_TestOnly = (): string => html` + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const bordered_TestOnly = (): string => html` + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const borderedZebra_TestOnly = (): string => html` + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const alignments_TestOnly = (): string => html` + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const disabledRows_TestOnly = (): string => html` + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const numbered_TestOnly = (): string => html` + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const richCellContent_TestOnly = (): string => html` + + + + + + + + + Chip + + cell + + + cell + + Chipbutton + chip + + + cell + chip + + chipchip + +`; + +export const layoutFixed_TestOnly = (): string => html` + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const rowSpanAndColSpan_TestOnly = (): string => html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + cell + cell + cell + + + cell + cell + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const rowSpanAndColSpanNumbered_TestOnly = (): string => html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + cell + cell + cell + + + cell + cell + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const rowSpanAndColSpan3_TestOnly = (): string => html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + cell + + + cell + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + +`; + +export const complexWithFooter_TestOnly = (): string => html` + + + + + + + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + cell + + + cell + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + foot + foot + + + foot + foot + +`; + +export const headersInRows_TestOnly = (): string => html` + + + cell + cell + cell + + + + cell + cell + cell + + + + cell + cell + cell + + + + cell + cell + cell + +`; + +export const headersInRowsAndHeadAndFooter_TestOnly = (): string => html` + + + + + + + + + cell + cell + cell + + + + cell + cell + cell + + + + cell + cell + cell + + + + cell + cell + cell + + + foot + foot + foot + foot + +`; + +export const singleSelection_TestOnly = (): string => html` + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const selectionModeMultipleAndSelectedOnLoad_TestOnly = (): string => html` + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + +`; + +export const selectionModeMultipleAndSelectedOnLoadWithMultipleFooterAndHeader_TestOnly = + (): string => html` + + + + + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + + 58% happiness + + + + 24,212 + 58% happiness + + + `; + +export const localized_TestOnly = (): string => html` + + + + + + + + + slot + + + + + + + + + cell + cell + cell + cell + cell + 34 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 53 + Happy + + Another thing + + cell + cell + cell + cell + cell + 25 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + +`; + +export const darkModeRTL_TestOnly = (): string => + html` + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + `; + +darkModeRTL_TestOnly.parameters = { modes: modesDarkDefault }; diff --git a/packages/calcite-components/src/components/table/table.tsx b/packages/calcite-components/src/components/table/table.tsx new file mode 100644 index 00000000000..cebaa86c466 --- /dev/null +++ b/packages/calcite-components/src/components/table/table.tsx @@ -0,0 +1,525 @@ +import { + Component, + Element, + Event, + EventEmitter, + h, + Host, + Listen, + Prop, + State, + VNode, + Watch, +} from "@stencil/core"; +import { Scale, SelectionMode } from "../interfaces"; +import { + LoadableComponent, + setComponentLoaded, + setUpLoadableComponent, +} from "../../utils/loadable"; +import { + connectMessages, + disconnectMessages, + setUpMessages, + T9nComponent, + updateMessages, +} from "../../utils/t9n"; +import { + connectLocalized, + disconnectLocalized, + LocalizedComponent, + numberStringFormatter, + NumberingSystem, +} from "../../utils/locale"; +import { TableLayout, TableRowFocusEvent } from "./interfaces"; +import { CSS, SLOTS } from "./resources"; +import { TableMessages } from "./assets/table/t9n"; +import { getUserAgentString } from "../../utils/browser"; + +/** + * @slot - A slot for adding `calcite-table-row` or nested `calcite-table` elements. + * @slot table-header - A slot for adding `calcite-table-row` containing `calcite-table-header` elements. + * @slot table-footer - A slot for adding `calcite-table-row` containing `calcite-table-cell` or `calcite-table-header` elements. + * @slot selection-actions - A slot for adding a `calcite-action-bar` or other components to display when `selectionMode` is not `"none"` and one or more `calcite-table-row` is selected. + */ + +@Component({ + tag: "calcite-table", + styleUrl: "table.scss", + shadow: true, + assetsDirs: ["assets"], +}) +export class Table implements LocalizedComponent, LoadableComponent, T9nComponent { + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** When `true`, displays borders in the component. */ + @Prop({ reflect: true }) bordered = false; + + /** Specifies an accessible title for the component. */ + @Prop() caption!: string; + + /** When `true`, number values are displayed with a group separator corresponding to the language and country format. */ + @Prop({ reflect: true }) groupSeparator = false; + + /** Specifies the layout of the component. */ + @Prop({ reflect: true }) layout: TableLayout = "auto"; + + /** When `true`, displays the position of the row in numeric form. */ + @Prop({ reflect: true }) numbered = false; + + /** Specifies the Unicode numeral system used by the component for localization. */ + @Prop({ reflect: true }) numberingSystem?: NumberingSystem; + + /** Specifies the page size of the component. When `true`, renders `calcite-pagination` */ + @Prop({ reflect: true }) pageSize = 0; + + /** Specifies the size of the component. */ + @Prop({ reflect: true }) scale: Scale = "m"; + + /** Specifies the selection mode of the component. */ + @Prop({ reflect: true }) selectionMode: Extract<"none" | "multiple" | "single", SelectionMode> = + "none"; + + /** When `true`, displays zebra styling in the component. */ + @Prop({ reflect: true }) zebra = false; + + @Watch("groupSeparator") + @Watch("numbered") + @Watch("numberingSystem") + @Watch("pageSize") + @Watch("scale") + @Watch("selectionMode") + handleNumberedChange(): void { + this.updateRows(); + } + + /** + * Specifies the component's selected items. + * + * @readonly + */ + @Prop({ mutable: true }) selectedItems: HTMLCalciteTableRowElement[] = []; + + /** + * Made into a prop for testing purposes only + * + * @internal + */ + // eslint-disable-next-line @stencil-community/strict-mutable -- updated by t9n module + @Prop({ mutable: true }) messages: TableMessages; + + /** + * Use this property to override individual strings used by the component. + */ + // eslint-disable-next-line @stencil-community/strict-mutable -- updated by t9n module + @Prop({ mutable: true }) messageOverrides: Partial; + + @Watch("messageOverrides") + onMessagesChange(): void { + /* wired up by t9n util */ + } + + // -------------------------------------------------------------------------- + // + // Private Properties + // + // -------------------------------------------------------------------------- + + @Element() el: HTMLCalciteTableElement; + + @State() colCount = 0; + + @State() pageStartRow = 1; + + @State() selectedCount = 0; + + /* Workaround for Safari https://bugs.webkit.org/show_bug.cgi?id=258430 https://bugs.webkit.org/show_bug.cgi?id=239478 */ + // ⚠️ browser-sniffing is not a best practice and should be avoided ⚠️ + @State() readCellContentsToAT: boolean; + + @State() defaultMessages: TableMessages; + + @State() effectiveLocale = ""; + + @Watch("effectiveLocale") + effectiveLocaleChange(): void { + updateMessages(this, this.effectiveLocale); + } + + private allRows: HTMLCalciteTableRowElement[]; + + private bodyRows: HTMLCalciteTableRowElement[]; + + private headRows: HTMLCalciteTableRowElement[]; + + private footRows: HTMLCalciteTableRowElement[]; + + private paginationEl: HTMLCalcitePaginationElement; + + private tableBodySlotEl: HTMLSlotElement; + + private tableHeadSlotEl: HTMLSlotElement; + + private tableFootSlotEl: HTMLSlotElement; + + //-------------------------------------------------------------------------- + // + // Lifecycle + // + //-------------------------------------------------------------------------- + + async componentWillLoad(): Promise { + setUpLoadableComponent(this); + await setUpMessages(this); + this.readCellContentsToAT = /safari/i.test(getUserAgentString()); + this.updateRows(); + } + + componentDidLoad(): void { + setComponentLoaded(this); + } + + connectedCallback(): void { + connectLocalized(this); + connectMessages(this); + } + + disconnectedCallback(): void { + disconnectLocalized(this); + disconnectMessages(this); + } + //-------------------------------------------------------------------------- + // + // Events + // + //-------------------------------------------------------------------------- + + /** Emits when the component's selected rows change. */ + @Event({ cancelable: false }) calciteTableSelect: EventEmitter; + + /** Emits when the component's page selection changes. */ + @Event({ cancelable: false }) calciteTablePageChange: EventEmitter; + + /** @internal */ + @Event({ cancelable: false }) + calciteInternalTableRowFocusChange: EventEmitter; + + //-------------------------------------------------------------------------- + // + // Event Listeners + // + //-------------------------------------------------------------------------- + + @Listen("calciteTableRowSelect") + calciteChipSelectListener(event: CustomEvent): void { + if (event.composedPath().includes(this.el)) { + this.setSelectedItems(event.target as HTMLCalciteTableRowElement); + } + } + + @Listen("calciteInternalTableRowFocusRequest") + calciteInternalTableRowFocusEvent(event: TableRowFocusEvent): void { + const cellPosition = event["detail"].cellPosition; + const rowPos = event["detail"].rowPosition; + const destination = event["detail"].destination; + const lastCell = event["detail"].lastCell; + + const visibleBody = this.bodyRows?.filter((row) => !row.hidden); + const visibleAll = this.allRows?.filter((row) => !row.hidden); + + const lastHeadRow = this.headRows[this.headRows.length - 1]?.positionAll; + const firstBodyRow = visibleBody[0]?.positionAll; + const lastBodyRow = visibleBody[visibleBody.length - 1]?.positionAll; + const firstFootRow = this.footRows[0]?.positionAll; + const lastTableRow = visibleAll[visibleAll.length - 1]?.positionAll; + + const leavingHeader = destination === "next" && rowPos === lastHeadRow; + const leavingFooter = destination === "previous" && rowPos === firstFootRow; + const enteringHeader = destination === "previous" && rowPos === firstBodyRow; + const enteringFooter = destination === "next" && rowPos === lastBodyRow; + + let rowPosition: number; + + switch (destination) { + case "first": + rowPosition = 0; + break; + case "last": + rowPosition = lastTableRow; + break; + case "next": + rowPosition = leavingHeader ? firstBodyRow : enteringFooter ? firstFootRow : rowPos + 1; + break; + case "previous": + rowPosition = leavingFooter ? lastBodyRow : enteringHeader ? lastHeadRow : rowPos - 1; + break; + } + + const destinationCount = this.allRows?.find( + (row) => row.positionAll === rowPosition + )?.cellCount; + + const adjustedPos = cellPosition > destinationCount ? destinationCount : cellPosition; + + if (rowPosition !== undefined) { + this.calciteInternalTableRowFocusChange.emit({ + cellPosition: adjustedPos, + rowPosition, + destination, + lastCell, + }); + } + } + // -------------------------------------------------------------------------- + // + // Private Methods + // + // -------------------------------------------------------------------------- + + private getSlottedRows = (el: HTMLSlotElement): HTMLCalciteTableRowElement[] => { + return el + ?.assignedElements({ flatten: true }) + ?.filter((el) => el?.matches("calcite-table-row")) as HTMLCalciteTableRowElement[]; + }; + + private updateRows = (): void => { + const headRows = this.getSlottedRows(this.tableHeadSlotEl) || []; + const bodyRows = this.getSlottedRows(this.tableBodySlotEl) || []; + const footRows = this.getSlottedRows(this.tableFootSlotEl) || []; + const allRows = [...headRows, ...bodyRows, ...footRows]; + + headRows?.forEach((row) => { + const position = headRows?.indexOf(row); + row.rowType = "head"; + row.positionSection = position; + row.positionSectionLocalized = this.localizeNumber((position + 1).toString()); + }); + + bodyRows?.forEach((row) => { + const position = bodyRows?.indexOf(row); + row.rowType = "body"; + row.positionSection = position; + row.positionSectionLocalized = this.localizeNumber((position + 1).toString()); + }); + + footRows?.forEach((row) => { + const position = footRows?.indexOf(row); + row.rowType = "foot"; + row.positionSection = position; + row.positionSectionLocalized = this.localizeNumber((position + 1).toString()); + }); + + allRows?.forEach((row) => { + row.selectionMode = this.selectionMode; + row.bodyRowCount = bodyRows?.length; + row.positionAll = allRows?.indexOf(row); + row.numbered = this.numbered; + row.scale = this.scale; + row.readCellContentsToAT = this.readCellContentsToAT; + }); + + const colCount = + headRows[0]?.cellCount || headRows[0]?.querySelectorAll("calcite-table-header")?.length; + + this.colCount = colCount; + this.headRows = headRows; + this.bodyRows = bodyRows; + this.footRows = footRows; + this.allRows = allRows; + + this.updateSelectedItems(); + this.paginateRows(); + }; + + private handlePaginationChange = (): void => { + const requestedItem = this.paginationEl?.startItem; + this.pageStartRow = requestedItem || 1; + this.calciteTablePageChange.emit(); + this.updateRows(); + }; + + private paginateRows = (): void => { + this.bodyRows?.forEach((row) => { + const rowPos = row.positionSection + 1; + const inView = rowPos >= this.pageStartRow && rowPos < this.pageStartRow + this.pageSize; + row.hidden = this.pageSize > 0 && !inView && !this.footRows.includes(row); + }); + }; + + private updateSelectedItems = (emit?: boolean): void => { + const selectedItems = this.bodyRows?.filter((el) => el.selected); + this.selectedItems = selectedItems; + this.selectedCount = selectedItems?.length; + this.allRows?.forEach((row) => { + row.selectedRowCount = this.selectedCount; + row.selectedRowCountLocalized = this.localizeNumber(this.selectedCount); + }); + if (emit) { + this.calciteTableSelect.emit(); + } + }; + + private handleDeselectAllRows = (): void => { + this.bodyRows?.forEach((row) => { + row.selected = false; + }); + this.updateSelectedItems(true); + }; + + private setSelectedItems = (elToMatch?: HTMLCalciteTableRowElement): void => { + this.bodyRows?.forEach((el) => { + if (elToMatch?.rowType === "head") { + el.selected = this.selectedCount !== this.bodyRows?.length; + } else { + el.selected = + elToMatch === el ? !el.selected : this.selectionMode === "multiple" ? el.selected : false; + } + }); + this.updateSelectedItems(true); + }; + + private localizeNumber = (value: number | string): string => { + numberStringFormatter.numberFormatOptions = { + locale: this.effectiveLocale, + numberingSystem: this.numberingSystem, + useGrouping: this.groupSeparator, + }; + + return numberStringFormatter.localize(value.toString()); + }; + + // -------------------------------------------------------------------------- + // + // Render Methods + // + // -------------------------------------------------------------------------- + + renderSelectionArea(): VNode { + const outOfViewCount = this.selectedItems?.filter((el) => el.hidden)?.length; + const localizedOutOfView = this.localizeNumber(outOfViewCount?.toString()); + const localizedSelectedCount = this.localizeNumber(this.selectedCount?.toString()); + const selectionText = `${localizedSelectedCount} ${this.messages.selected}`; + const outOfView = `${localizedOutOfView} ${this.messages.hiddenSelected}`; + + return ( +
+ 0 ? "brand" : "neutral"} + scale={this.scale} + value={selectionText} + > + {selectionText} + + {outOfViewCount > 0 && ( + + {localizedOutOfView} + + )} + {this.selectedCount > 0 && ( + + {this.messages.clear} + + )} +
+ +
+
+ ); + } + + renderPaginationArea(): VNode { + return ( +
+ (this.paginationEl = el)} + /> +
+ ); + } + + renderTHead(): VNode { + return ( + + (this.tableHeadSlotEl = el as HTMLSlotElement)} + /> + + ); + } + + renderTBody(): VNode { + return ( + + (this.tableBodySlotEl = el as HTMLSlotElement)} + /> + + ); + } + + renderTFoot(): VNode { + return ( + + (this.tableFootSlotEl = el as HTMLSlotElement)} + /> + + ); + } + + render(): VNode { + return ( + +
+ {this.selectionMode !== "none" && this.renderSelectionArea()} +
+ + + {this.renderTHead()} + {this.renderTBody()} + {this.renderTFoot()} +
{this.caption}
+
+ {this.pageSize > 0 && this.renderPaginationArea()} +
+
+ ); + } +} diff --git a/packages/calcite-components/src/components/table/usage/Advanced.md b/packages/calcite-components/src/components/table/usage/Advanced.md new file mode 100644 index 00000000000..1fe9eb68b24 --- /dev/null +++ b/packages/calcite-components/src/components/table/usage/Advanced.md @@ -0,0 +1,70 @@ +A complex table component, with selection modes and slotted actions, pagination, and various display options configured. + +```html + + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + + 24,212 + 58% happiness + + + +``` diff --git a/packages/calcite-components/src/components/table/usage/Basic.md b/packages/calcite-components/src/components/table/usage/Basic.md new file mode 100644 index 00000000000..46f49fa287b --- /dev/null +++ b/packages/calcite-components/src/components/table/usage/Basic.md @@ -0,0 +1,30 @@ +A simple meter component. + +```html + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + +``` diff --git a/packages/calcite-components/src/demos/table.html b/packages/calcite-components/src/demos/table.html new file mode 100644 index 00000000000..e402b84e3ad --- /dev/null +++ b/packages/calcite-components/src/demos/table.html @@ -0,0 +1,3990 @@ + + + + + + + Table + + + + + + + +

A11y testing

+ + + + + + + + + + + slot + + + + + + + + + cell + cell + cell + cell + cell + test 1 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 2 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 3 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 4 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 5 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 7 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 8 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 9 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + test 10 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 11 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 13 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 14 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 15 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 16 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 17 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 18 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 19 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 20 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + test 21 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 22 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 23 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 53 + Happy + + Another thing + + cell + cell + cell + cell + cell + 25 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 53 + Happy + + Another thing + + cell + cell + cell + cell + cell + 25 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + + + +

Interactive

+ +
+
+ Features + + Numbered + Zebra + Bordered + +
+
+ Selection Mode + + none + single + multiple + +
+
+ Scale + + S + M + L + +
+
+ Layout + + Auto + Fixed + +
+
+ pageSize + + + +
+ +
+ Themes + + Calcite (Default) + Mint Glacier + Ranger Station + Lavender Field + +
+
+ + + + + + + + + + + slot + + + + + + + + + cell + cell + cell + cell + cell + test 1 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 2 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 3 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 4 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 5 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 7 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 8 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 9 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + test 10 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 11 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 13 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 14 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 15 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 16 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 17 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 18 + Happy + + Another thing + + cell + cell + cell + cell + cell + test 19 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 20 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + test 21 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 22 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + test 23 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 53 + Happy + + Another thing + + cell + cell + cell + cell + cell + 25 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 53 + Happy + + Another thing + + cell + cell + cell + cell + cell + 25 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 12 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 78 + Happy + + Another thing + + + + +

Localized numbers

+ + + + + + + + + + + slot + + + + + + + + + cell + cell + cell + cell + cell + 34 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 53 + Happy + + Another thing + + cell + cell + cell + cell + cell + 25 + Happy + + Another thing + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + cell + cell + cell + cell + cell + 1643 + Happy + + Another thing + + + + + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 62 + Happy + + Another thing + + cell + cell + cell + cell + cell + 6 + Happy + + Another thing + + + + + cell + cell + cell + cell + 262 + Sad + + Another thing + + + + cell + cell + cell + cell + cell + 63 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 120 + Happy + + Another thing + + + + cell + cell + cell + cell + cell + 987 + Happy + + Another thing + + + +

Simple

+ + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

zebra

+ + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Bordered

+ + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Bordered-zebra

+ + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Various alignments

+ + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Disabled rows

+ + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Numbered

+ + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Numbered

+ + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + foot + foot + foot + foot + + + +

With rich cell content

+ + + + + + + + + + Chip + + cell + + + cell + + Chipbutton + chip + + + cell + chip + + chipchip + + + foot + foot + foot + foot + + + +

Layout fixed

+ + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Using row-span and col-span

+ + + + + + + cell + cell + + + cell + cell + + + cell + cell + cell + cell + cell + + + cell + cell + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Using row-span and col-span and numbered

+ + + + + + + cell + cell + + + cell + cell + + + cell + cell + cell + cell + cell + + + cell + cell + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Using row-span and col-span

+ + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + cell + + + cell + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + +

Multiple headers using col-span

+ + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

Complex keyboard test with multiple headers, selection, pagination using col-span

+ + + + + + + + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + cell + + + cell + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + foot + foot + + + foot + foot + + + +

Headers in rows

+ + + + cell + cell + cell + + + + cell + cell + cell + + + + cell + cell + cell + + + + cell + cell + cell + + + +

Headers in rows and table-head

+ + + + + + + + + + + + + + + + cell + cell + cell + + + + cell + cell + cell + + + + cell + cell + cell + + + + cell + cell + cell + + + + foot + foot + foot + + + + foot + foot + foot + + + +

selection-mode single

+ + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

selection-mode multiple with selected at load

+ + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + foot + foot + foot + foot + + + +

selection-mode multiple with multiple headers, footers, pageSize

+ + + + + + + + + + + + + + cell + cell + + + cell + cell + + + cell + cell + + + cell + cell + + + foot + foot + foot + foot + + + foot + foot + + + + foot + foot + foot + + + + foot + + + +

selection-mode multiple and numbered including out of view

+ + + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + +

selection-mode multiple and numbered including out of view and footer

+ + + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + + 24,212 + 58% happiness + + + +

selection-mode multiple and numbered including out of view and multiple header and footer

+ + + + + + + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + + 58% happiness + + + + 24,212 + 58% happiness + + + +

With selection-mode multiple and rich cell content

+ + + + + + + + + + Chip + + cell + + + cell + + Chipbutton + chip + + + cell + chip + + chipchip + + + +

Handle keyboard navigation with disabled rows in various places

+

+ Expect up / down to skip to next non-disabled - expect "page up / down / cntrl home + end" to focus next + available non-disabled +

+ + + + + + + + + + + + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + + cell + cell + cell + cell + + +
+ + + diff --git a/packages/calcite-components/src/index.html b/packages/calcite-components/src/index.html index 55485167f6b..e2bed63436e 100644 --- a/packages/calcite-components/src/index.html +++ b/packages/calcite-components/src/index.html @@ -431,6 +431,12 @@

Calcite demo

+
+ + + +
+
diff --git a/t9nmanifest.txt b/t9nmanifest.txt index 0fa58306416..38bb14869aa 100644 --- a/t9nmanifest.txt +++ b/t9nmanifest.txt @@ -34,6 +34,9 @@ packages\calcite-components\src\components\rating\assets\rating\t9n packages\calcite-components\src\components\scrim\assets\scrim\t9n packages\calcite-components\src\components\shell-panel\assets\shell-panel\t9n packages\calcite-components\src\components\tab-title\assets\tab-title\t9n +packages\calcite-components\src\components\table\assets\table\t9n +packages\calcite-components\src\components\table-cell\assets\table-cell\t9n +packages\calcite-components\src\components\table-header\assets\table-header\t9n packages\calcite-components\src\components\text-area\assets\text-area\t9n packages\calcite-components\src\components\time-picker\assets\time-picker\t9n packages\calcite-components\src\components\tip\assets\tip\t9n From 7a92b3ab83f5a517a786d4e6e5a4ef4c5094044c Mon Sep 17 00:00:00 2001 From: Adam Tirella Date: Thu, 31 Aug 2023 11:56:47 -0700 Subject: [PATCH 14/35] docs(table): Update usage (#7640) ## Summary Fix for copy-paste in usage file. --- packages/calcite-components/src/components/table/usage/Basic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/calcite-components/src/components/table/usage/Basic.md b/packages/calcite-components/src/components/table/usage/Basic.md index 46f49fa287b..89eadc21dd4 100644 --- a/packages/calcite-components/src/components/table/usage/Basic.md +++ b/packages/calcite-components/src/components/table/usage/Basic.md @@ -1,4 +1,4 @@ -A simple meter component. +A simple table component. ```html From 7005cce95835193c8fc8ab41ad47b64abe9de66e Mon Sep 17 00:00:00 2001 From: Ben Elan Date: Thu, 31 Aug 2023 12:01:16 -0700 Subject: [PATCH 15/35] fix: make sure components are defined in environments like in codesandbox (#7632) **Related Issue:** #7575 ## Summary CodeSandbox exposes `process`, which makes it look like NodeJS. The only way to determine it should be treated as the browser is the non-standard value they use for `process.platform`. ref: https://nodejs.org/api/process.html#processplatform --- .../src/auto-define.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/calcite-components-react/src/auto-define.ts b/packages/calcite-components-react/src/auto-define.ts index eb5fb21ed84..235701e957a 100644 --- a/packages/calcite-components-react/src/auto-define.ts +++ b/packages/calcite-components-react/src/auto-define.ts @@ -1,11 +1,18 @@ -const isBrowser = (): boolean => - ![typeof window, typeof document, typeof location].includes("undefined") && - [typeof process, typeof global].includes("undefined") && - window.location === location && - window.document === document; +// CodeSandbox exposes `process`, which makes it look like NodeJS. The only way to determine it should be +// be treated as the browser is the non-standard value they use for `process.platform`. +// https://nodejs.org/api/process.html#processplatform +type CodeSandboxWorkaround = NodeJS.Platform | "browser"; + +// https://github.com/flexdinesh/browser-or-node/blob/master/src/index.js +const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; +const isNode = + typeof process !== "undefined" && + process.versions != null && + process.versions.node != null && + (process?.platform as CodeSandboxWorkaround) !== "browser"; export function autoDefine(component: string): () => Promise | undefined { - if (isBrowser()) { + if (isBrowser && !isNode) { return async () => (await import(`@esri/calcite-components/dist/components/${component}.js`)).defineCustomElement(); } return undefined; From a1db718f7f53b766154a3f12e5859de65eeae617 Mon Sep 17 00:00:00 2001 From: Anveshreddy mekala Date: Thu, 31 Aug 2023 14:23:30 -0500 Subject: [PATCH 16/35] feat(input-date-picker): normalize year to current century for user typed values only (#7638) **Related Issue:** #7588 ## Summary This will normalize two digit year typed by user to current century. Ex: If the user types `01/01/20` the value will be corrected to `01/01/2020` when user focus out of the input field or press a `Enter` key. --- .../input-date-picker.e2e.ts | 17 ++-------- .../input-date-picker.stories.ts | 13 -------- .../input-date-picker/input-date-picker.tsx | 32 +++---------------- 3 files changed, 6 insertions(+), 56 deletions(-) diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts b/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts index 3cb4e147b1c..136550e1626 100644 --- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts +++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts @@ -869,7 +869,7 @@ describe("calcite-input-date-picker", () => { expect(changeEvent).toHaveReceivedEventTimes(1); }); - it("should normalize year to current century when value is parsed as attribute", async () => { + it("should not normalize year to current century when value is parsed as attribute", async () => { const page = await newE2EPage(); await page.setContent( html`` @@ -878,7 +878,7 @@ describe("calcite-input-date-picker", () => { const element = await page.find("calcite-input-date-picker"); const changeEvent = await page.spyOnEvent("calciteInputDatePickerChange"); - expect(await element.getProperty("value")).toBe("2020-01-01"); + expect(await element.getProperty("value")).toBe("0020-01-01"); expect(await element.getProperty("valueAsDate")).toBeDefined(); expect(changeEvent).toHaveReceivedEventTimes(0); }); @@ -905,18 +905,5 @@ describe("calcite-input-date-picker", () => { expect(await element.getProperty("value")).toEqual(["2020-01-01", "2020-02-02"]); expect(changeEvent).toHaveReceivedEventTimes(2); }); - - it("should normalize year to current century when value is changed programmatically in range", async () => { - const page = await newE2EPage(); - await page.setContent(""); - const element = await page.find("calcite-input-date-picker"); - const changeEvent = await page.spyOnEvent("calciteInputDatePickerChange"); - - element.setProperty("value", ["00-03-07", "00-03-08"]); - await page.waitForChanges(); - - expect(await element.getProperty("value")).toEqual(["2000-03-07", "2000-03-08"]); - expect(changeEvent).toHaveReceivedEventTimes(0); - }); }); }); diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.stories.ts b/packages/calcite-components/src/components/input-date-picker/input-date-picker.stories.ts index e8f71139845..3383705be8c 100644 --- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.stories.ts +++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.stories.ts @@ -96,16 +96,3 @@ export const darkModeRTL_TestOnly = (): string => html`
`; darkModeRTL_TestOnly.parameters = { modes: modesDarkDefault }; - -export const normalizeYearWithGermanLocale_TestOnly = (): string => html` -
- -
- -`; diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx b/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx index ba4a62b2575..a008dd3139d 100644 --- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx +++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx @@ -157,16 +157,8 @@ export class InputDatePicker let newValueAsDate: Date | Date[]; if (Array.isArray(newValue)) { - if (isTwoDigitYear(newValue[0]) || isTwoDigitYear(newValue[1])) { - this.value = newValue.map((val) => this.getNormalizedDate(val)); - return; - } newValueAsDate = getValueAsDateRange(newValue); } else if (newValue) { - if (isTwoDigitYear(newValue)) { - this.value = this.getNormalizedDate(newValue); - return; - } newValueAsDate = dateFromISO(newValue); } else { newValueAsDate = undefined; @@ -283,11 +275,6 @@ export class InputDatePicker */ @Prop({ reflect: true }) name: string; - /** - * Normalizes year to current century. - */ - @Prop({ reflect: true }) normalizeYear = false; - /** * Specifies the Unicode numeral system used by the component for localization. This property cannot be dynamically changed. * @@ -450,17 +437,9 @@ export class InputDatePicker const { open } = this; open && this.openHandler(open); if (Array.isArray(this.value)) { - if (isTwoDigitYear(this.value[0]) || isTwoDigitYear(this.value[1])) { - this.value = this.value.map((val) => this.getNormalizedDate(val)); - return; - } this.valueAsDate = getValueAsDateRange(this.value); } else if (this.value) { try { - if (isTwoDigitYear(this.value)) { - this.value = this.getNormalizedDate(this.value); - return; - } this.valueAsDate = dateFromISO(this.value); } catch (error) { this.warnAboutInvalidValue(this.value); @@ -1051,13 +1030,13 @@ export class InputDatePicker const newStartDate = valueIsArray ? valueAsDate[0] : null; let newStartDateISO = valueIsArray ? dateToISO(newStartDate) : ""; - if (newStartDateISO && isTwoDigitYear(newStartDateISO)) { + if (newStartDateISO) { newStartDateISO = this.getNormalizedDate(newStartDateISO); } const newEndDate = valueIsArray ? valueAsDate[1] : null; let newEndDateISO = valueIsArray ? dateToISO(newEndDate) : ""; - if (newEndDateISO && isTwoDigitYear(newEndDateISO)) { + if (newEndDateISO) { newEndDateISO = this.getNormalizedDate(newEndDateISO); } @@ -1092,10 +1071,7 @@ export class InputDatePicker const oldValue = this.value; let newValue = dateToISO(value as Date); - - if (isTwoDigitYear(newValue)) { - newValue = this.getNormalizedDate(newValue); - } + newValue = this.getNormalizedDate(newValue); if (newValue === oldValue) { return; @@ -1150,7 +1126,7 @@ export class InputDatePicker return ""; } - if (!this.normalizeYear) { + if (!isTwoDigitYear(value)) { return value; } From b120fa9ac3b9195131d0bf66c7aadf7062d011b9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 31 Aug 2023 19:52:07 +0000 Subject: [PATCH 17/35] chore: release next --- package-lock.json | 8 ++++---- packages/calcite-components-react/CHANGELOG.md | 6 ++++++ packages/calcite-components-react/package.json | 4 ++-- packages/calcite-components/CHANGELOG.md | 7 +++++++ packages/calcite-components/package.json | 2 +- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index b0fe36ee11b..d43c498a0de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40443,7 +40443,7 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "1.7.0-next.16", + "version": "1.7.0-next.17", "license": "SEE LICENSE.md", "dependencies": { "@floating-ui/dom": "1.5.1", @@ -40470,10 +40470,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "1.7.0-next.16", + "version": "1.7.0-next.17", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^1.7.0-next.16" + "@esri/calcite-components": "^1.7.0-next.17" }, "peerDependencies": { "react": ">=16.7", @@ -42704,7 +42704,7 @@ "@esri/calcite-components-react": { "version": "file:packages/calcite-components-react", "requires": { - "@esri/calcite-components": "^1.7.0-next.16" + "@esri/calcite-components": "^1.7.0-next.17" } }, "@esri/calcite-design-tokens": { diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index dca78a71fc9..744e3a8fc9e 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.17](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.16...@esri/calcite-components-react@1.7.0-next.17) (2023-08-31) + +### Bug Fixes + +- make sure components are defined in environments like in codesandbox ([#7632](https://github.com/Esri/calcite-design-system/issues/7632)) ([7005cce](https://github.com/Esri/calcite-design-system/commit/7005cce95835193c8fc8ab41ad47b64abe9de66e)), closes [#7575](https://github.com/Esri/calcite-design-system/issues/7575) + ## [1.7.0-next.16](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.15...@esri/calcite-components-react@1.7.0-next.16) (2023-08-31) **Note:** Version bump only for package @esri/calcite-components-react diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index ce5639a6c95..202069e4f8e 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,7 +1,7 @@ { "name": "@esri/calcite-components-react", "sideEffects": false, - "version": "1.7.0-next.16", + "version": "1.7.0-next.17", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", @@ -20,7 +20,7 @@ "dist/" ], "dependencies": { - "@esri/calcite-components": "^1.7.0-next.16" + "@esri/calcite-components": "^1.7.0-next.17" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index 7e4a8a0e5d2..37900acf3d8 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.17](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.16...@esri/calcite-components@1.7.0-next.17) (2023-08-31) + +### Features + +- **input-date-picker:** normalize year to current century for user typed values only ([#7638](https://github.com/Esri/calcite-design-system/issues/7638)) ([a1db718](https://github.com/Esri/calcite-design-system/commit/a1db718f7f53b766154a3f12e5859de65eeae617)), closes [#7588](https://github.com/Esri/calcite-design-system/issues/7588) +- **table:** Add Table and related components ([#7607](https://github.com/Esri/calcite-design-system/issues/7607)) ([b067e72](https://github.com/Esri/calcite-design-system/commit/b067e728f999931f234f2b1b07af425c9bb11329)), closes [#6697](https://github.com/Esri/calcite-design-system/issues/6697) + ## [1.7.0-next.16](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.15...@esri/calcite-components@1.7.0-next.16) (2023-08-31) ### Bug Fixes diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index dc6520793e6..86707173f8f 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "1.7.0-next.16", + "version": "1.7.0-next.17", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", From 4d66e5bfdecd1bcc292b89a19f7a69256139f2fa Mon Sep 17 00:00:00 2001 From: JC Franco Date: Thu, 31 Aug 2023 12:58:13 -0700 Subject: [PATCH 18/35] docs(contributing): update the commit message format example URL (#7641) **Related Issue:** N/A ## Summary This updates the URL to Stencil's contributing doc, which was moved to the root. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 32fdbb7abf5..d3b13bac618 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -207,7 +207,7 @@ johndoe/feature/add-something-to-modal This project follows [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/), which are used to generate the changelog. Be sure to provide clear and sufficient information in commit messages. This is important because the commit messages are used to automatically update the changelog. -[Stencil's contributing document](https://github.com/ionic-team/stencil/blob/master/.github/CONTRIBUTING.md#commit-message-format) explains this in great detail, so please refer to this for more details and examples. +[Stencil's contributing document](https://github.com/ionic-team/stencil/blob/main/CONTRIBUTING.md#commit-message-format) explains this in great detail, so please refer to this for more details and examples. ## Breaking changes From fc2c720bc69db81075b7e021d69c4cb7e2c2b5a9 Mon Sep 17 00:00:00 2001 From: Eliza Khachatryan Date: Thu, 31 Aug 2023 13:42:59 -0700 Subject: [PATCH 19/35] refactor(stepper, stepper-item): `getElementProp` is refactored out in favor of inheritable props set directly on parent (#7593) **Related Issue:** #6038 ## Summary `getElementProp` is refactored out across child components as an outdated pattern in favor of inheritable props set directly on the parent. Instead of the `stepper-item` looking up the parent for `icon, layout, numbered, and scale`, these get set by the `stepper` parent. The logic for setting these props thus moves to the parent, getting rid of the `getElementProp` altogether. The parent component gets a `mutationObserver` to do this as well as watchers for when it needs to modify the children. Inherited props addressed: - [x] icon - [x] layout - [x] numbered - [x] scale --------- Co-authored-by: Matt Driscoll Co-authored-by: github-actions[bot] Co-authored-by: JC Franco Co-authored-by: Anveshreddy mekala --- .../components/stepper-item/stepper-item.tsx | 52 ++++---- .../src/components/stepper/stepper.e2e.ts | 112 +++++++++++------- .../src/components/stepper/stepper.tsx | 32 ++++- 3 files changed, 126 insertions(+), 70 deletions(-) diff --git a/packages/calcite-components/src/components/stepper-item/stepper-item.tsx b/packages/calcite-components/src/components/stepper-item/stepper-item.tsx index d73822e53b3..6a0527219aa 100644 --- a/packages/calcite-components/src/components/stepper-item/stepper-item.tsx +++ b/packages/calcite-components/src/components/stepper-item/stepper-item.tsx @@ -12,7 +12,7 @@ import { VNode, Watch, } from "@stencil/core"; -import { getElementProp, toAriaBoolean } from "../../utils/dom"; +import { toAriaBoolean } from "../../utils/dom"; import { Layout, Scale } from "../interfaces"; import { connectInteractive, @@ -81,27 +81,9 @@ export class StepperItem implements InteractiveComponent, LocalizedComponent, Lo /** A description for the component. Displays below the header text. */ @Prop() description: string; - // internal props inherited from wrapping calcite-stepper - /** Defines the layout of the component. */ - /** @internal */ - @Prop({ reflect: true, mutable: true }) layout: Extract<"horizontal" | "vertical", Layout> = - "horizontal"; - - /** When `true`, displays a status icon in the component's heading. */ - /** @internal */ - @Prop({ mutable: true }) icon = false; - /** When `true`, the icon will be flipped when the element direction is right-to-left (`"rtl"`). */ @Prop({ reflect: true }) iconFlipRtl = false; - /** When `true`, displays the step number in the component's heading. */ - /** @internal */ - @Prop({ mutable: true }) numbered = false; - - /** Specifies the size of the component. */ - /** @internal */ - @Prop({ reflect: true, mutable: true }) scale: Scale = "m"; - /** * @internal */ @@ -113,6 +95,34 @@ export class StepperItem implements InteractiveComponent, LocalizedComponent, Lo this.registerStepperItem(); } + /** + * When `true`, displays a status icon in the `calcite-stepper-item` heading inherited from parent `calcite-stepper`. + * + * @internal + */ + @Prop() icon = false; + + /** + * Specifies the layout of the `calcite-stepper-item` inherited from parent `calcite-stepper`, defaults to `horizontal`. + * + * @internal + */ + @Prop({ reflect: true }) layout: Extract<"horizontal" | "vertical", Layout>; + + /** + * When `true`, displays the step number in the `calcite-stepper-item` heading inherited from parent `calcite-stepper`. + * + * @internal + */ + @Prop() numbered = false; + + /** + * Specifies the size of the component inherited from the `calcite-stepper`, defaults to `m`. + * + * @internal + */ + @Prop({ reflect: true }) scale: Scale = "m"; + //-------------------------------------------------------------------------- // // Internal State/Props @@ -175,10 +185,6 @@ export class StepperItem implements InteractiveComponent, LocalizedComponent, Lo componentWillLoad(): void { setUpLoadableComponent(this); - this.icon = getElementProp(this.el, "icon", false); - this.numbered = getElementProp(this.el, "numbered", false); - this.layout = getElementProp(this.el, "layout", false); - this.scale = getElementProp(this.el, "scale", "m"); this.parentStepperEl = this.el.parentElement as HTMLCalciteStepperElement; this.itemPosition = this.getItemPosition(); this.registerStepperItem(); diff --git a/packages/calcite-components/src/components/stepper/stepper.e2e.ts b/packages/calcite-components/src/components/stepper/stepper.e2e.ts index aecbf0cc603..0d46f302023 100644 --- a/packages/calcite-components/src/components/stepper/stepper.e2e.ts +++ b/packages/calcite-components/src/components/stepper/stepper.e2e.ts @@ -1,10 +1,52 @@ import { E2EPage, newE2EPage } from "@stencil/core/testing"; -import { renders, hidden } from "../../tests/commonTests"; +import { defaults, hidden, reflects, renders } from "../../tests/commonTests"; import { html } from "../../../support/formatting"; import { NumberStringFormatOptions } from "../../utils/locale"; // todo test the automatic setting of first item to selected describe("calcite-stepper", () => { + describe("defaults", () => { + defaults("calcite-stepper", [ + { + propertyName: "icon", + defaultValue: false, + }, + { + propertyName: "layout", + defaultValue: "horizontal", + }, + { + propertyName: "numbered", + defaultValue: false, + }, + { + propertyName: "scale", + defaultValue: "m", + }, + ]); + }); + + describe("reflects", () => { + reflects("calcite-stepper", [ + { + propertyName: "icon", + value: true, + }, + { + propertyName: "layout", + value: "horizontal", + }, + { + propertyName: "numbered", + value: true, + }, + { + propertyName: "scale", + value: "m", + }, + ]); + }); + describe("renders", () => { renders( html` @@ -25,50 +67,32 @@ describe("calcite-stepper", () => { ); }); - it("renders default props when none are provided", async () => { - const page = await newE2EPage(); - await page.setContent(html` - -
Step 1 content
-
- -
Step 2 content
-
- -
Step 3 content
-
- -
Step 4 content
-
-
`); - const element = await page.find("calcite-stepper"); - expect(element).toEqualAttribute("layout", "horizontal"); - expect(element).toEqualAttribute("scale", "m"); - expect(element).not.toHaveAttribute("numbered"); - expect(element).not.toHaveAttribute("icon"); - }); - - it("renders requested props when valid props are provided", async () => { + it("inheritable props: `icon`, `layout`, `numbered`, and `scale` get passed to items from parents", async () => { const page = await newE2EPage(); - await page.setContent(html` - -
Step 1 content
-
- -
Step 2 content
-
- -
Step 3 content
-
- -
Step 4 content
-
-
`); - const element = await page.find("calcite-stepper"); - expect(element).toEqualAttribute("layout", "vertical"); - expect(element).toEqualAttribute("scale", "l"); - expect(element).toHaveAttribute("numbered"); - expect(element).toHaveAttribute("icon"); + await page.setContent(html` + + +
Step 1 content
+
+ +
Step 2 content
+
+ +
Step 3 content
+
+ +
Step 4 content
+
+
+ `); + const stepperItems = await page.findAll("calcite-stepper-items"); + + stepperItems.forEach(async (item) => { + expect(await item.getProperty("icon")).toBe(true); + expect(await item.getProperty("layout")).toBe("vertical"); + expect(await item.getProperty("scale")).toBe("l"); + expect(await item.getProperty("numbered")).toBe(true); + }); }); // eslint-disable-next-line jest/no-disabled-tests diff --git a/packages/calcite-components/src/components/stepper/stepper.tsx b/packages/calcite-components/src/components/stepper/stepper.tsx index bc5ecec8156..bb15cee58bb 100644 --- a/packages/calcite-components/src/components/stepper/stepper.tsx +++ b/packages/calcite-components/src/components/stepper/stepper.tsx @@ -15,6 +15,7 @@ import { focusElementInGroup } from "../../utils/dom"; import { NumberingSystem } from "../../utils/locale"; import { Layout, Scale } from "../interfaces"; import { StepperItemChangeEventDetail, StepperItemKeyEventDetail } from "./interfaces"; +import { createObserver } from "../../utils/observers"; /** * @slot - A slot for adding `calcite-stepper-item` elements. @@ -40,6 +41,17 @@ export class Stepper { /** When `true`, displays the step number in the `calcite-stepper-item` heading. */ @Prop({ reflect: true }) numbered = false; + /** Specifies the size of the component. */ + @Prop({ reflect: true }) scale: Scale = "m"; + + @Watch("icon") + @Watch("layout") + @Watch("numbered") + @Watch("scale") + handleItemPropChange(): void { + this.updateItems(); + } + /** * Specifies the Unicode numeral system used by the component for localization. */ @@ -57,9 +69,6 @@ export class Stepper { */ @Prop({ mutable: true }) selectedItem: HTMLCalciteStepperItemElement = null; - /** Specifies the size of the component. */ - @Prop({ reflect: true }) scale: Scale = "m"; - //-------------------------------------------------------------------------- // // Events @@ -86,6 +95,12 @@ export class Stepper { // Lifecycle // //-------------------------------------------------------------------------- + + connectedCallback(): void { + this.mutationObserver?.observe(this.el, { childList: true }); + this.updateItems(); + } + componentDidLoad(): void { // if no stepper items are set as active, default to the first one if (typeof this.currentPosition !== "number") { @@ -258,6 +273,17 @@ export class Stepper { /** keep track of the currently active item position */ private currentPosition: number; + private mutationObserver = createObserver("mutation", () => this.updateItems()); + + private updateItems(): void { + this.el.querySelectorAll("calcite-stepper-item").forEach((item) => { + item.icon = this.icon; + item.numbered = this.numbered; + item.layout = this.layout; + item.scale = this.scale; + }); + } + //-------------------------------------------------------------------------- // // Private Methods From b5170b6e9e5b0b0f7aa9f825c799a8cc506095a7 Mon Sep 17 00:00:00 2001 From: JC Franco Date: Thu, 31 Aug 2023 14:45:50 -0700 Subject: [PATCH 20/35] fix(accordion, accordion-item): improve a11y (#7560) **Related Issue:** #5553 ## Summary Updated HTML to improve a11y. --- .../accordion-item/accordion-item.e2e.ts | 28 ++++++++++++++++++- .../accordion-item/accordion-item.tsx | 8 ++++-- .../components/accordion-item/resources.ts | 5 ++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/calcite-components/src/components/accordion-item/accordion-item.e2e.ts b/packages/calcite-components/src/components/accordion-item/accordion-item.e2e.ts index bb785037968..38594afb89c 100644 --- a/packages/calcite-components/src/components/accordion-item/accordion-item.e2e.ts +++ b/packages/calcite-components/src/components/accordion-item/accordion-item.e2e.ts @@ -1,5 +1,7 @@ +import { newE2EPage } from "@stencil/core/testing"; import { accessible, renders, slots, hidden } from "../../tests/commonTests"; -import { SLOTS } from "./resources"; +import { CSS, IDS, SLOTS } from "./resources"; +import { html } from "../../../support/formatting"; describe("calcite-accordion-item", () => { describe("renders", () => { @@ -17,4 +19,28 @@ describe("calcite-accordion-item", () => { describe("slots", () => { slots("calcite-accordion-item", SLOTS); }); + + it("properly uses ARIA and roles", async () => { + // this test covers a11y relationships not reported by axe-core/accessible test helper + + const page = await newE2EPage(); + await page.setContent(html``); + + const headerContent = await page.find(`calcite-accordion-item >>> .${CSS.headerContent}`); + + expect(headerContent.getAttribute("aria-expanded")).toBe("false"); + expect(headerContent.getAttribute("aria-controls")).toBe(IDS.section); + expect(headerContent.getAttribute("role")).toBe("button"); + + const content = await page.find(`calcite-accordion-item >>> .${CSS.content}`); + + expect(content.getAttribute("aria-labelledby")).toBe(IDS.sectionToggle); + expect(await content.getProperty("id")).toBe(IDS.section); + + const accordionItem = await page.find(`calcite-accordion-item`); + accordionItem.setProperty("expanded", true); + await page.waitForChanges(); + + expect(headerContent.getAttribute("aria-expanded")).toBe("true"); + }); }); diff --git a/packages/calcite-components/src/components/accordion-item/accordion-item.tsx b/packages/calcite-components/src/components/accordion-item/accordion-item.tsx index 528db159a08..939fa9cd328 100644 --- a/packages/calcite-components/src/components/accordion-item/accordion-item.tsx +++ b/packages/calcite-components/src/components/accordion-item/accordion-item.tsx @@ -21,7 +21,7 @@ import { toAriaBoolean, } from "../../utils/dom"; import { CSS_UTILITY } from "../../utils/resources"; -import { SLOTS, CSS } from "./resources"; +import { SLOTS, CSS, IDS } from "./resources"; import { FlipContext, Position, Scale, SelectionMode } from "../interfaces"; import { RequestedItem } from "./interfaces"; @@ -174,8 +174,10 @@ export class AccordionItem implements ConditionalSlotComponent {
{this.renderActionsStart()}
{this.renderActionsEnd()}
-
+
-
+
); diff --git a/packages/calcite-components/src/components/accordion-item/resources.ts b/packages/calcite-components/src/components/accordion-item/resources.ts index eec57658ec1..32de2307438 100644 --- a/packages/calcite-components/src/components/accordion-item/resources.ts +++ b/packages/calcite-components/src/components/accordion-item/resources.ts @@ -18,3 +18,8 @@ export const CSS = { iconEnd: "icon--end", headerContainer: "header-container", }; + +export const IDS = { + section: "section", + sectionToggle: "section-toggle", +}; From b52b5756e6a9b0f668d6cd64ed0df1deff9963f0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 31 Aug 2023 22:08:37 +0000 Subject: [PATCH 21/35] chore: release next --- package-lock.json | 8 ++++---- packages/calcite-components-react/CHANGELOG.md | 4 ++++ packages/calcite-components-react/package.json | 4 ++-- packages/calcite-components/CHANGELOG.md | 6 ++++++ packages/calcite-components/package.json | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index d43c498a0de..bc1170cb41d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40443,7 +40443,7 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "1.7.0-next.17", + "version": "1.7.0-next.18", "license": "SEE LICENSE.md", "dependencies": { "@floating-ui/dom": "1.5.1", @@ -40470,10 +40470,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "1.7.0-next.17", + "version": "1.7.0-next.18", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^1.7.0-next.17" + "@esri/calcite-components": "^1.7.0-next.18" }, "peerDependencies": { "react": ">=16.7", @@ -42704,7 +42704,7 @@ "@esri/calcite-components-react": { "version": "file:packages/calcite-components-react", "requires": { - "@esri/calcite-components": "^1.7.0-next.17" + "@esri/calcite-components": "^1.7.0-next.18" } }, "@esri/calcite-design-tokens": { diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index 744e3a8fc9e..0520b71611f 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.18](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.17...@esri/calcite-components-react@1.7.0-next.18) (2023-08-31) + +**Note:** Version bump only for package @esri/calcite-components-react + ## [1.7.0-next.17](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.16...@esri/calcite-components-react@1.7.0-next.17) (2023-08-31) ### Bug Fixes diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index 202069e4f8e..baeb23a3b97 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,7 +1,7 @@ { "name": "@esri/calcite-components-react", "sideEffects": false, - "version": "1.7.0-next.17", + "version": "1.7.0-next.18", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", @@ -20,7 +20,7 @@ "dist/" ], "dependencies": { - "@esri/calcite-components": "^1.7.0-next.17" + "@esri/calcite-components": "^1.7.0-next.18" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index 37900acf3d8..78c8f8f130b 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.18](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.17...@esri/calcite-components@1.7.0-next.18) (2023-08-31) + +### Bug Fixes + +- **accordion, accordion-item:** improve a11y ([#7560](https://github.com/Esri/calcite-design-system/issues/7560)) ([b5170b6](https://github.com/Esri/calcite-design-system/commit/b5170b6e9e5b0b0f7aa9f825c799a8cc506095a7)), closes [#5553](https://github.com/Esri/calcite-design-system/issues/5553) + ## [1.7.0-next.17](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.16...@esri/calcite-components@1.7.0-next.17) (2023-08-31) ### Features diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index 86707173f8f..2e121bb1a93 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "1.7.0-next.17", + "version": "1.7.0-next.18", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", From cd66a6dfa1179c656efbb149359d13a2e1ab2dd9 Mon Sep 17 00:00:00 2001 From: Ben Elan Date: Thu, 31 Aug 2023 16:29:04 -0700 Subject: [PATCH 22/35] feat(input-number): add integer property (#7646) **Related Issue:** #6706 ## Summary Add an `integer` property to `calcite-input-number` which prevents decimals and exponential notation. --- .../input-number/input-number.e2e.ts | 41 +++++++++++++++---- .../components/input-number/input-number.tsx | 27 +++++++++--- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/packages/calcite-components/src/components/input-number/input-number.e2e.ts b/packages/calcite-components/src/components/input-number/input-number.e2e.ts index a0706e9d015..caba06ef9c2 100644 --- a/packages/calcite-components/src/components/input-number/input-number.e2e.ts +++ b/packages/calcite-components/src/components/input-number/input-number.e2e.ts @@ -1,4 +1,4 @@ -import { E2EPage, newE2EPage } from "@stencil/core/testing"; +import { E2EElement, E2EPage, EventSpy, newE2EPage } from "@stencil/core/testing"; import { KeyInput } from "puppeteer"; import { html } from "../../../support/formatting"; import { @@ -544,8 +544,8 @@ describe("calcite-input-number", () => { }); describe("mouse events on arrow buttons", () => { - let input; - let calciteInputNumberInput; + let input: E2EElement; + let calciteInputNumberInput: EventSpy; beforeEach(async () => { await page.setContent(html``); @@ -1631,7 +1631,7 @@ describe("calcite-input-number", () => { expect(await button.getProperty("disabled")).toBe(true); expect(await input.getProperty("disabled")).toBe(false); - await input.setProperty("disabled", true); + input.setProperty("disabled", true); await input.callMethod("setFocus"); await page.waitForChanges(); await typeNumberValue(page, "2"); @@ -1640,7 +1640,7 @@ describe("calcite-input-number", () => { expect(await button.getProperty("disabled")).toBe(true); expect(await input.getProperty("disabled")).toBe(true); - await input.setProperty("disabled", false); + input.setProperty("disabled", false); await page.waitForChanges(); await input.callMethod("setFocus"); await page.waitForChanges(); @@ -1650,7 +1650,7 @@ describe("calcite-input-number", () => { expect(await button.getProperty("disabled")).toBe(true); expect(await input.getProperty("disabled")).toBe(false); - await button.setProperty("disabled", false); + button.setProperty("disabled", false); await page.waitForChanges(); await input.callMethod("setFocus"); await page.waitForChanges(); @@ -1660,7 +1660,7 @@ describe("calcite-input-number", () => { expect(await button.getProperty("disabled")).toBe(false); expect(await input.getProperty("disabled")).toBe(false); - await input.setProperty("disabled", true); + input.setProperty("disabled", true); await page.waitForChanges(); await input.callMethod("setFocus"); await page.waitForChanges(); @@ -1671,6 +1671,33 @@ describe("calcite-input-number", () => { expect(await input.getProperty("disabled")).toBe(true); }); + it("integer property prevents decimals and exponential notation", async () => { + const page = await newE2EPage(); + await page.setContent(``); + + const input = await page.find("calcite-input-number"); + const numberHorizontalItemUp = await page.find( + "calcite-input-number >>> .number-button-item[data-adjustment='up']" + ); + + await input.callMethod("setFocus"); + await page.waitForChanges(); + + expect(await input.getProperty("value")).toBe("12"); // test initial value + + await typeNumberValue(page, "3.4e-5"); + await page.waitForChanges(); + expect(await input.getProperty("value")).toBe("12345"); // test user input + + input.setProperty("value", "-9.8e-7"); + await page.waitForChanges(); + expect(await input.getProperty("value")).toBe("-987"); // test directly setting value + + await numberHorizontalItemUp.click(); + await page.waitForChanges(); + expect(await input.getProperty("value")).toBe("-986"); // test incrementing + }); + describe("is form-associated", () => { formAssociated("calcite-input-number", { testValue: 5, diff --git a/packages/calcite-components/src/components/input-number/input-number.tsx b/packages/calcite-components/src/components/input-number/input-number.tsx index 4a7a7a5854c..9b7c829cfb7 100644 --- a/packages/calcite-components/src/components/input-number/input-number.tsx +++ b/packages/calcite-components/src/components/input-number/input-number.tsx @@ -148,6 +148,9 @@ export class InputNumber /** When `true`, the icon will be flipped when the element direction is right-to-left (`"rtl"`). */ @Prop({ reflect: true }) iconFlipRtl = false; + /** When `true`, restricts the component to integer numbers only and disables exponential notation. */ + @Prop() integer = false; + /** Accessible name for the component's button or hyperlink. */ @Prop() label: string; @@ -540,7 +543,9 @@ export class InputNumber ): void { const { value } = this; const adjustment = direction === "up" ? 1 : -1; - const inputStep = this.step === "any" ? 1 : Math.abs(this.step || 1); + const stepHandleInteger = + this.integer && this.step !== "any" ? Math.round(this.step) : this.step; + const inputStep = stepHandleInteger === "any" ? 1 : Math.abs(stepHandleInteger || 1); const inputVal = new BigDecimal(value !== "" ? value : "0"); const nudgedValue = inputVal.add(`${inputStep * adjustment}`); @@ -616,7 +621,10 @@ export class InputNumber }; const delocalizedValue = numberStringFormatter.delocalize(value); if (nativeEvent.inputType === "insertFromPaste") { - if (!isValidNumber(delocalizedValue)) { + if ( + !isValidNumber(delocalizedValue) || + (this.integer && (delocalizedValue.includes("e") || delocalizedValue.includes("."))) + ) { nativeEvent.preventDefault(); } this.setNumberValue({ @@ -675,7 +683,7 @@ export class InputNumber useGrouping: this.groupSeparator, }; - if (event.key === numberStringFormatter.decimal) { + if (event.key === numberStringFormatter.decimal && !this.integer) { if (!this.value && !this.childNumberEl.value) { return; } @@ -683,7 +691,7 @@ export class InputNumber return; } } - if (/[eE]/.test(event.key)) { + if (/[eE]/.test(event.key) && !this.integer) { if (!this.value && !this.childNumberEl.value) { return; } @@ -836,9 +844,16 @@ export class InputNumber const isValueDeleted = this.previousValue?.length > value.length || this.value?.length > value.length; - const hasTrailingDecimalSeparator = value.charAt(value.length - 1) === "."; + + const valueHandleInteger = this.integer ? value.replace(/[e.]/g, "") : value; + + const hasTrailingDecimalSeparator = + valueHandleInteger.charAt(valueHandleInteger.length - 1) === "."; + const sanitizedValue = - hasTrailingDecimalSeparator && isValueDeleted ? value : sanitizeNumberString(value); + hasTrailingDecimalSeparator && isValueDeleted + ? valueHandleInteger + : sanitizeNumberString(valueHandleInteger); const newValue = value && !sanitizedValue From 4653581e72dfa0c235e799bf6039ff6bc4c9ef8a Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Thu, 31 Aug 2023 16:37:27 -0700 Subject: [PATCH 23/35] fix(list, sortable-list, value-list): Emit calciteListOrderChange when dragging between lists (#7614) **Related Issue:** #7046 ## Summary TLDR: Basically, we need to pause draggable component connected/disconnected lifecycle events while a component is being dragged. Having these lifecycle methods kick off during a drag causes SortableJS errors. - SortableComponent - Refactors logic to prevent any SortableJS component from doing its lifecycle logic when a component is being dragged - Previously, it was preventing all SortableJS components Sortable from being destroyed or created but that was causing issues by not emitting events when an item was moved from one list to another. - The nested component check that was previously being used isn't ideal because two different lists don't have to be nested to drag items between each other. - We need all lists to still continue emitting events when necessary, we just don't want their lifecycle methods to kick off when an item is being dragged. Otherwise, JS errors are thrown. - Components - Updates SortableComponent components to not do any lifecycle callbacks when an item is being dragged to prevent any JS errors that SortableJS was throwing. - This was because in connectedCallback, sortable components were setting up the sortable instance, connecting the observer, modifying items, etc. We don't want the component to do this while an item is being dragged. - The same thing as above was happening on disconnectedCallback. - This fix stops all those errors that occurred while dragging an item from one list to another. ## Assumptions It is reasonable to not do any lifecycle events for any draggable component while a component is being dragged --- .../src/components/list-item/list-item.tsx | 2 +- .../src/components/list/list.e2e.ts | 36 +++++++++- .../src/components/list/list.tsx | 36 ++++++---- .../sortable-list/sortable-list.tsx | 15 +++- .../src/components/value-list/value-list.tsx | 15 +++- .../src/utils/sortableComponent.ts | 70 ++++++++----------- 6 files changed, 108 insertions(+), 66 deletions(-) diff --git a/packages/calcite-components/src/components/list-item/list-item.tsx b/packages/calcite-components/src/components/list-item/list-item.tsx index 57695f934b4..5672455bf07 100644 --- a/packages/calcite-components/src/components/list-item/list-item.tsx +++ b/packages/calcite-components/src/components/list-item/list-item.tsx @@ -236,7 +236,7 @@ export class ListItem @Listen("calciteInternalListItemGroupDefaultSlotChange") @Listen("calciteInternalListDefaultSlotChange") - handleCalciteInternalListDefaultSlotChanges(event: CustomEvent): void { + handleCalciteInternalListDefaultSlotChanges(event: CustomEvent): void { event.stopPropagation(); this.handleOpenableChange(this.defaultSlotEl); } diff --git a/packages/calcite-components/src/components/list/list.e2e.ts b/packages/calcite-components/src/components/list/list.e2e.ts index 0dccb24cc46..48998a14b73 100755 --- a/packages/calcite-components/src/components/list/list.e2e.ts +++ b/packages/calcite-components/src/components/list/list.e2e.ts @@ -5,7 +5,7 @@ import { E2EPage, newE2EPage } from "@stencil/core/testing"; import { debounceTimeout } from "./resources"; import { CSS } from "../list-item/resources"; import { DEBOUNCE_TIMEOUT as FILTER_DEBOUNCE_TIMEOUT } from "../filter/resources"; -import { dragAndDrop, isElementFocused } from "../../tests/utils"; +import { GlobalTestProps, dragAndDrop, isElementFocused } from "../../tests/utils"; const placeholder = placeholderImage({ width: 140, @@ -472,9 +472,21 @@ describe("calcite-list", () => { return page; } + type TestWindow = GlobalTestProps<{ + calledTimes: number; + }>; + it("works using a mouse", async () => { const page = await createSimpleList(); + // Workaround for page.spyOnEvent() failing due to drag event payload being serialized and there being circular JSON structures from the payload elements. See: https://github.com/Esri/calcite-design-system/issues/7643 + await page.$eval("calcite-list", (list: HTMLCalciteListElement) => { + (window as TestWindow).calledTimes = 0; + list.addEventListener("calciteListOrderChange", () => { + (window as TestWindow).calledTimes++; + }); + }); + await dragAndDrop( page, { @@ -490,6 +502,9 @@ describe("calcite-list", () => { const [first, second] = await page.findAll("calcite-list-item"); expect(await first.getProperty("value")).toBe("two"); expect(await second.getProperty("value")).toBe("one"); + await page.waitForChanges(); + + expect(await page.evaluate(() => (window as TestWindow).calledTimes)).toBe(1); }); it("supports dragging items between lists", async () => { @@ -517,6 +532,19 @@ describe("calcite-list", () => { `); + await page.waitForChanges(); + + // Workaround for page.spyOnEvent() failing due to drag event payload being serialized and there being circular JSON structures from the payload elements. See: https://github.com/Esri/calcite-design-system/issues/7643 + await page.evaluate(() => { + (window as TestWindow).calledTimes = 0; + const lists = document.querySelectorAll("calcite-list"); + lists.forEach((list) => + list.addEventListener("calciteListOrderChange", () => { + (window as TestWindow).calledTimes++; + }) + ); + }); + await dragAndDrop( page, { @@ -571,6 +599,8 @@ describe("calcite-list", () => { expect(await seventh.getProperty("value")).toBe("c"); expect(await eight.getProperty("value")).toBe("e"); expect(await ninth.getProperty("value")).toBe("f"); + + expect(await page.evaluate(() => (window as TestWindow).calledTimes)).toBe(2); }); it("works using a keyboard", async () => { @@ -586,7 +616,7 @@ describe("calcite-list", () => { let totalMoves = 0; - const listOrderChangeSpy = await page.spyOnEvent("calciteListOrderChange"); + const eventSpy = await page.spyOnEvent("calciteListOrderChange"); async function assertKeyboardMove( arrowKey: "ArrowDown" | "ArrowUp", @@ -603,7 +633,7 @@ describe("calcite-list", () => { expect(await itemsAfter[i].getProperty("value")).toBe(expectedValueOrder[i]); } - expect(listOrderChangeSpy).toHaveReceivedEventTimes(++totalMoves); + expect(eventSpy).toHaveReceivedEventTimes(++totalMoves); } await assertKeyboardMove("ArrowDown", ["two", "one", "three"]); diff --git a/packages/calcite-components/src/components/list/list.tsx b/packages/calcite-components/src/components/list/list.tsx index ba60916ca83..ea20806d094 100755 --- a/packages/calcite-components/src/components/list/list.tsx +++ b/packages/calcite-components/src/components/list/list.tsx @@ -11,7 +11,7 @@ import { VNode, Watch, } from "@stencil/core"; -import Sortable, { SortableEvent } from "sortablejs"; +import Sortable from "sortablejs"; import { debounce } from "lodash-es"; import { slotChangeHasAssignedElement, toAriaBoolean } from "../../utils/dom"; import { @@ -27,10 +27,11 @@ import { MAX_COLUMNS } from "../list-item/resources"; import { getListItemChildren, updateListItemChildren } from "../list-item/utils"; import { CSS, debounceTimeout, SelectionAppearance, SLOTS } from "./resources"; import { - DragEvent, + DragDetail, connectSortableComponent, disconnectSortableComponent, SortableComponent, + dragActive, } from "../../utils/sortableComponent"; import { SLOTS as STACK_SLOTS } from "../stack/resources"; @@ -73,12 +74,12 @@ export class List implements InteractiveComponent, LoadableComponent, SortableCo /** * When provided, the method will be called to determine whether the element can move from the list. */ - @Prop() canPull: (event: DragEvent) => boolean; + @Prop() canPull: (detail: DragDetail) => boolean; /** * When provided, the method will be called to determine whether the element can be added from another list. */ - @Prop() canPut: (event: DragEvent) => boolean; + @Prop() canPut: (detail: DragDetail) => boolean; /** * When `true`, `calcite-list-item`s are sortable via a draggable button. @@ -191,12 +192,12 @@ export class List implements InteractiveComponent, LoadableComponent, SortableCo /** * Emitted when the order of the list has changed. */ - @Event({ cancelable: false }) calciteListOrderChange: EventEmitter; + @Event({ cancelable: false }) calciteListOrderChange: EventEmitter; /** * Emitted when the default slot has changes in order to notify parent lists. */ - @Event({ cancelable: false }) calciteInternalListDefaultSlotChange: EventEmitter; + @Event({ cancelable: false }) calciteInternalListDefaultSlotChange: EventEmitter; @Listen("calciteInternalFocusPreviousItem") handleCalciteInternalFocusPreviousItem(event: CustomEvent): void { @@ -294,14 +295,22 @@ export class List implements InteractiveComponent, LoadableComponent, SortableCo //-------------------------------------------------------------------------- connectedCallback(): void { + if (dragActive(this)) { + return; + } + this.connectObserver(); this.updateListItems(); this.setUpSorting(); connectInteractive(this); - this.parentListEl = this.el.parentElement.closest("calcite-list"); + this.setParentList(); } disconnectedCallback(): void { + if (dragActive(this)) { + return; + } + this.disconnectObserver(); disconnectSortableComponent(this); disconnectInteractive(this); @@ -476,16 +485,15 @@ export class List implements InteractiveComponent, LoadableComponent, SortableCo this.connectObserver(); } - onDragSort(event: SortableEvent): void { + onDragSort(detail: DragDetail): void { + this.setParentList(); this.updateListItems(); - const { from, item, to } = event; + this.calciteListOrderChange.emit(detail); + } - this.calciteListOrderChange.emit({ - dragEl: item, - fromEl: from, - toEl: to, - }); + private setParentList(): void { + this.parentListEl = this.el.parentElement?.closest("calcite-list"); } private handleDefaultSlotChange = (event: Event): void => { diff --git a/packages/calcite-components/src/components/sortable-list/sortable-list.tsx b/packages/calcite-components/src/components/sortable-list/sortable-list.tsx index 5883145855a..347f95e1bc6 100644 --- a/packages/calcite-components/src/components/sortable-list/sortable-list.tsx +++ b/packages/calcite-components/src/components/sortable-list/sortable-list.tsx @@ -11,10 +11,11 @@ import { HandleNudge } from "../handle/interfaces"; import { Layout } from "../interfaces"; import { CSS } from "./resources"; import { - DragEvent, + DragDetail, connectSortableComponent, disconnectSortableComponent, SortableComponent, + dragActive, } from "../../utils/sortableComponent"; import { focusElement } from "../../utils/dom"; @@ -36,12 +37,12 @@ export class SortableList implements InteractiveComponent, SortableComponent { /** * When provided, the method will be called to determine whether the element can move from the list. */ - @Prop() canPull: (event: DragEvent) => boolean; + @Prop() canPull: (detail: DragDetail) => boolean; /** * When provided, the method will be called to determine whether the element can be added from another list. */ - @Prop() canPut: (event: DragEvent) => boolean; + @Prop() canPut: (detail: DragDetail) => boolean; /** * Specifies which items inside the element should be draggable. @@ -100,12 +101,20 @@ export class SortableList implements InteractiveComponent, SortableComponent { // -------------------------------------------------------------------------- connectedCallback(): void { + if (dragActive(this)) { + return; + } + this.setUpSorting(); this.beginObserving(); connectInteractive(this); } disconnectedCallback(): void { + if (dragActive(this)) { + return; + } + disconnectInteractive(this); disconnectSortableComponent(this); this.endObserving(); diff --git a/packages/calcite-components/src/components/value-list/value-list.tsx b/packages/calcite-components/src/components/value-list/value-list.tsx index 04aad6bd779..25780cc7cf6 100644 --- a/packages/calcite-components/src/components/value-list/value-list.tsx +++ b/packages/calcite-components/src/components/value-list/value-list.tsx @@ -62,10 +62,11 @@ import { ValueListMessages } from "./assets/value-list/t9n"; import { CSS, ICON_TYPES } from "./resources"; import { getHandleAndItemElement, getScreenReaderText } from "./utils"; import { - DragEvent, + DragDetail, connectSortableComponent, disconnectSortableComponent, SortableComponent, + dragActive, } from "../../utils/sortableComponent"; import { focusElement } from "../../utils/dom"; @@ -103,12 +104,12 @@ export class ValueList< /** * When provided, the method will be called to determine whether the element can move from the list. */ - @Prop() canPull: (event: DragEvent) => boolean; + @Prop() canPull: (detail: DragDetail) => boolean; /** * When provided, the method will be called to determine whether the element can be added from another list. */ - @Prop() canPut: (event: DragEvent) => boolean; + @Prop() canPut: (detail: DragDetail) => boolean; /** * When `true`, `calcite-value-list-item`s are sortable via a draggable button. @@ -238,6 +239,10 @@ export class ValueList< // -------------------------------------------------------------------------- connectedCallback(): void { + if (dragActive(this)) { + return; + } + connectInteractive(this); connectLocalized(this); connectMessages(this); @@ -261,6 +266,10 @@ export class ValueList< } disconnectedCallback(): void { + if (dragActive(this)) { + return; + } + disconnectInteractive(this); disconnectSortableComponent(this); disconnectLocalized(this); diff --git a/packages/calcite-components/src/utils/sortableComponent.ts b/packages/calcite-components/src/utils/sortableComponent.ts index c4133c9e3d7..143df5c46cf 100644 --- a/packages/calcite-components/src/utils/sortableComponent.ts +++ b/packages/calcite-components/src/utils/sortableComponent.ts @@ -1,9 +1,7 @@ import Sortable from "sortablejs"; -import { containsCrossShadowBoundary } from "./dom"; const sortableComponentSet = new Set(); -const inactiveSortableComponentSet = new WeakSet(); -export interface DragEvent { +export interface DragDetail { toEl: HTMLElement; fromEl: HTMLElement; dragEl: HTMLElement; @@ -46,27 +44,27 @@ export interface SortableComponent { /** * Whether the element can move from the list. */ - canPull: (event: DragEvent) => boolean; + canPull: (detail: DragDetail) => boolean; /** * Whether the element can be added from another list. */ - canPut: (event: DragEvent) => boolean; + canPut: (detail: DragDetail) => boolean; /** * Called by any change to the list (add / update / remove). */ - onDragSort: (event: Sortable.SortableEvent) => void; + onDragSort: (detail: DragDetail) => void; /** - * Element dragging started. + * Called when a sortable component drag starts. */ - onDragStart?: (event: Sortable.SortableEvent) => void; + onDragStart: () => void; /** - * Element dragging ended. + * Called when a sortable component drag ends. */ - onDragEnd?: (event: Sortable.SortableEvent) => void; + onDragEnd: () => void; } /** @@ -78,10 +76,6 @@ export function connectSortableComponent(component: SortableComponent): void { disconnectSortableComponent(component); sortableComponentSet.add(component); - if (inactiveSortableComponentSet.has(component)) { - return; - } - const dataIdAttr = "id"; const { group, handleSelector: handle, dragSelector: draggable } = component; @@ -100,16 +94,16 @@ export function connectSortableComponent(component: SortableComponent): void { }, }), handle, - onStart: (event) => { - onSortingStart(component); - component.onDragStart(event); + onStart: () => { + dragState.active = true; + onDragStart(); }, - onEnd: (event) => { - onSortingEnd(component); - component.onDragEnd(event); + onEnd: () => { + dragState.active = false; + onDragEnd(); }, - onSort: (event) => { - component.onDragSort(event); + onSort: ({ from: fromEl, item: dragEl, to: toEl }) => { + component.onDragSort({ fromEl, dragEl, toEl }); }, }); } @@ -122,34 +116,26 @@ export function connectSortableComponent(component: SortableComponent): void { export function disconnectSortableComponent(component: SortableComponent): void { sortableComponentSet.delete(component); - if (inactiveSortableComponentSet.has(component)) { - return; - } - component.sortable?.destroy(); component.sortable = null; } -function getNestedSortableComponents(activeComponent: SortableComponent): SortableComponent[] { - return Array.from(sortableComponentSet).filter( - (component) => component !== activeComponent && containsCrossShadowBoundary(activeComponent.el, component.el) - ); -} +const dragState: { active: boolean } = { active: false }; /** - * Helper to handle nested SortableComponents on `Sortable.onStart`. + * Helper to determine if dragging is currently active. * - * @param {SortableComponent} activeComponent - The active sortable component. + * @param component The sortable component. + * @returns {boolean} a boolean value. */ -function onSortingStart(activeComponent: SortableComponent): void { - getNestedSortableComponents(activeComponent).forEach((component) => inactiveSortableComponentSet.add(component)); +export function dragActive(component: SortableComponent): boolean { + return component.dragEnabled && dragState.active; } -/** - * Helper to handle nested SortableComponents on `Sortable.onEnd`. - * - * @param {SortableComponent} activeComponent - The active sortable component. - */ -function onSortingEnd(activeComponent: SortableComponent): void { - getNestedSortableComponents(activeComponent).forEach((component) => inactiveSortableComponentSet.delete(component)); +function onDragStart(): void { + Array.from(sortableComponentSet).forEach((component) => component.onDragStart()); +} + +function onDragEnd(): void { + Array.from(sortableComponentSet).forEach((component) => component.onDragEnd()); } From 38be8f88f5768571941f00767ada42d1f8f6a520 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 31 Aug 2023 23:59:06 +0000 Subject: [PATCH 24/35] chore: release next --- package-lock.json | 8 ++++---- packages/calcite-components-react/CHANGELOG.md | 4 ++++ packages/calcite-components-react/package.json | 4 ++-- packages/calcite-components/CHANGELOG.md | 10 ++++++++++ packages/calcite-components/package.json | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index bc1170cb41d..2abf915fcd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40443,7 +40443,7 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "1.7.0-next.18", + "version": "1.7.0-next.19", "license": "SEE LICENSE.md", "dependencies": { "@floating-ui/dom": "1.5.1", @@ -40470,10 +40470,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "1.7.0-next.18", + "version": "1.7.0-next.19", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^1.7.0-next.18" + "@esri/calcite-components": "^1.7.0-next.19" }, "peerDependencies": { "react": ">=16.7", @@ -42704,7 +42704,7 @@ "@esri/calcite-components-react": { "version": "file:packages/calcite-components-react", "requires": { - "@esri/calcite-components": "^1.7.0-next.18" + "@esri/calcite-components": "^1.7.0-next.19" } }, "@esri/calcite-design-tokens": { diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index 0520b71611f..a1491976c40 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.19](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.18...@esri/calcite-components-react@1.7.0-next.19) (2023-08-31) + +**Note:** Version bump only for package @esri/calcite-components-react + ## [1.7.0-next.18](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.17...@esri/calcite-components-react@1.7.0-next.18) (2023-08-31) **Note:** Version bump only for package @esri/calcite-components-react diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index baeb23a3b97..113c3c54cd4 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,7 +1,7 @@ { "name": "@esri/calcite-components-react", "sideEffects": false, - "version": "1.7.0-next.18", + "version": "1.7.0-next.19", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", @@ -20,7 +20,7 @@ "dist/" ], "dependencies": { - "@esri/calcite-components": "^1.7.0-next.18" + "@esri/calcite-components": "^1.7.0-next.19" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index 78c8f8f130b..cc074a7a641 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.19](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.18...@esri/calcite-components@1.7.0-next.19) (2023-08-31) + +### Features + +- **input-number:** add integer property ([#7646](https://github.com/Esri/calcite-design-system/issues/7646)) ([cd66a6d](https://github.com/Esri/calcite-design-system/commit/cd66a6dfa1179c656efbb149359d13a2e1ab2dd9)), closes [#6706](https://github.com/Esri/calcite-design-system/issues/6706) + +### Bug Fixes + +- **list, sortable-list, value-list:** Emit calciteListOrderChange when dragging between lists ([#7614](https://github.com/Esri/calcite-design-system/issues/7614)) ([4653581](https://github.com/Esri/calcite-design-system/commit/4653581e72dfa0c235e799bf6039ff6bc4c9ef8a)), closes [#7046](https://github.com/Esri/calcite-design-system/issues/7046) + ## [1.7.0-next.18](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.17...@esri/calcite-components@1.7.0-next.18) (2023-08-31) ### Bug Fixes diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index 2e121bb1a93..a1b05ba0590 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "1.7.0-next.18", + "version": "1.7.0-next.19", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", From 197adfe763491d728521d4265bc9b2c413be66c9 Mon Sep 17 00:00:00 2001 From: JC Franco Date: Thu, 31 Aug 2023 17:04:05 -0700 Subject: [PATCH 25/35] feat(flow): add support for custom flow-item elements (#7608) **Related Issue:** #6237 ## Summary This enables `flow` to work with custom components using shadow DOM wrapping `flow-item`. Custom components will have to implement the [`FlowItemLike`](https://github.com/Esri/calcite-design-system/pull/7608/files#diff-82c222ab365cde13a1f1288d936611519dfd9bee1e283164b260ca554c04a191R3-R7) interface and set the new `custom-item-selectors`/`customItemSelectors` attr/prop to target custom components (see [E2E test](https://github.com/Esri/calcite-design-system/pull/7608/files#diff-9b86e64de24dfca441533c63ae0f6834bff10bffbde23fd8bb3989a2259e356cR315-R387) for vanilla JS example). **Note**: `customItemSelectors` is intentionally marked internal since we don't have any documentation yet on developing custom components, which this is meant to support. We could additionally hide `FlowItemLike` in the doc site to avoid confusion until we have more documentation on this use case. I'm open to suggestions on this. @geospatialem @macandcheese @driskull --- .../calcite-components/conventions/README.md | 30 ++++ .../src/components/flow/flow.e2e.ts | 134 +++++++++++++++ .../src/components/flow/flow.tsx | 31 +++- .../src/components/flow/interfaces.ts | 6 + .../calcite-components/src/demos/flow.html | 155 +++++++++--------- 5 files changed, 270 insertions(+), 86 deletions(-) diff --git a/packages/calcite-components/conventions/README.md b/packages/calcite-components/conventions/README.md index f86368d715d..bff430b437f 100644 --- a/packages/calcite-components/conventions/README.md +++ b/packages/calcite-components/conventions/README.md @@ -409,3 +409,33 @@ The [`globalAttributes`](../src/utils/globalAttributes.ts) util was specifically ### BigDecimal `BigDecimal` is a [number util](https://github.com/Esri/calcite-design-system/blob/main/packages/calcite-components/src/utils/number.ts) that helps with [arbitrary precision arithmetic](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic). The util is adopted from a [Stack Overflow answer](https://stackoverflow.com/a/66939244) with some small changes. There are some usage examples in [`number.spec.ts`](../src/utils/number.spec.ts). + +### Custom child element support + +In order to support certain architectures, parent components might need to handle custom elements that wrap their expected child items within shadow DOM that would prevent discovery when querying the DOM. + +For such cases, the following pattern will enable developers to create custom child/item components and have them work seamlessly with parent components. + +#### Parent component + +- Must provide a `customItemSelectors` property to allow querying for custom elements in addition to their expected children. +- An interface for `HTMLElement` must be created in the parent's `interfaces.d.ts` file, where the necessary child APIs must be extracted. + **`parent/interfaces.d.ts`** + ```ts + type ChildComponentLike = Pick & HTMLElement; + ``` + **`parent/parent.tsx`** + ```tsx + @Prop() selectedItem: HTMLChildComponentElement | ChildComponentLike; + ``` + +#### Custom child component + +- Must implement the element interface expected by the parent (e.g., `ChildComponentLike`). + +#### Notes + +- This pattern should be applied sparingly and on a case-by-case basis. +- We can refine this pattern as we go on, but additional modifications needed to handle the custom items workflow will be considered out-of-scope and thus not supported. +- Until we have documentation covering creating custom elements, `customItemSelectors` must be made internal and any `ChildComponentLike` types must be excluded from the doc. +- Please refer to https://github.com/Esri/calcite-design-system/pull/7608/ as an example on how this pattern is applied. diff --git a/packages/calcite-components/src/components/flow/flow.e2e.ts b/packages/calcite-components/src/components/flow/flow.e2e.ts index 34b4239495c..ce2f649becd 100755 --- a/packages/calcite-components/src/components/flow/flow.e2e.ts +++ b/packages/calcite-components/src/components/flow/flow.e2e.ts @@ -5,6 +5,7 @@ import { accessible, focusable, hidden, renders } from "../../tests/commonTests" import { CSS as ITEM_CSS } from "../flow-item/resources"; import { CSS } from "./resources"; import { isElementFocused } from "../../tests/utils"; +import { FlowItemLikeElement } from "./interfaces"; describe("calcite-flow", () => { describe("renders", () => { @@ -339,4 +340,137 @@ describe("calcite-flow", () => { expect(await items[4].getProperty("showBackButton")).toBe(false); }); }); + + it("supports custom flow-items", async () => { + const page = await newE2EPage(); + await page.setContent(html` + + +

😃

+
+ +

🥸

+
+ +

😃

+
+
+ `); + + await page.evaluate(async () => { + class CustomFlowItem extends HTMLElement implements FlowItemLikeElement { + private flowItemEl: HTMLCalciteFlowItemElement; + + constructor() { + super(); + const shadow = this.attachShadow({ mode: "open" }); + + shadow.innerHTML = ` + + + + + `; + + this.flowItemEl = shadow.getElementById("internalFlowItem") as HTMLCalciteFlowItemElement; + } + + connectedCallback(): void { + this.flowItemEl.setAttribute("heading", this.getAttribute("heading")); + this.flowItemEl.setAttribute("show-back-button", this.getAttribute("show-back-button")); + this.flowItemEl.setAttribute("menu-open", this.getAttribute("menu-open")); + } + + get heading(): string { + return this.getAttribute("heading"); + } + + set heading(value: string) { + this.flowItemEl.heading = value; + } + + get hidden(): boolean { + return this.hasAttribute("hidden"); + } + + set hidden(value: boolean) { + this.toggleAttribute("hidden", value); + this.flowItemEl.toggleAttribute("hidden", value); + } + + get menuOpen(): boolean { + return this.hasAttribute("menu-open"); + } + + set menuOpen(value: boolean) { + this.toggleAttribute("menu-open", value); + this.flowItemEl.menuOpen = value; + } + + get showBackButton(): boolean { + return this.hasAttribute("show-back-button"); + } + + set showBackButton(value: boolean) { + this.toggleAttribute("show-back-button", value); + this.flowItemEl.showBackButton = value; + } + + async beforeBack(): Promise { + // no op + } + + async setFocus(): Promise { + await this.flowItemEl.setFocus(); + } + } + + customElements.define("custom-flow-item", CustomFlowItem); + }); + + const flow = await page.find("calcite-flow"); + const displayedItemSelector = "calcite-flow > *:not([hidden])"; + let displayedItem = await page.find(displayedItemSelector); + + expect(await flow.getProperty("childElementCount")).toBe(3); + expect(displayedItem.id).toBe("third"); + + await page.evaluate( + async (displayedItemSelector: string, ITEM_CSS) => { + document + .querySelector(displayedItemSelector) + .shadowRoot.querySelector(`.${ITEM_CSS.backButton}`) + .click(); + }, + displayedItemSelector, + ITEM_CSS + ); + await page.waitForChanges(); + + displayedItem = await page.find(displayedItemSelector); + expect(await flow.getProperty("childElementCount")).toBe(2); + expect(displayedItem.id).toBe("second"); + + await page.evaluate( + async (displayedItemSelector: string, ITEM_CSS) => { + document + .querySelector(displayedItemSelector) + .shadowRoot.querySelector("calcite-flow-item") + .shadowRoot.querySelector(`.${ITEM_CSS.backButton}`) + .click(); + }, + displayedItemSelector, + ITEM_CSS + ); + await page.waitForChanges(); + + displayedItem = await page.find(displayedItemSelector); + expect(await flow.getProperty("childElementCount")).toBe(1); + expect(displayedItem.id).toBe("first"); + }); }); diff --git a/packages/calcite-components/src/components/flow/flow.tsx b/packages/calcite-components/src/components/flow/flow.tsx index e4482fdfeda..a15f0f32e5d 100755 --- a/packages/calcite-components/src/components/flow/flow.tsx +++ b/packages/calcite-components/src/components/flow/flow.tsx @@ -1,6 +1,6 @@ -import { Component, Element, h, Listen, Method, State, VNode } from "@stencil/core"; +import { Component, Element, h, Listen, Method, Prop, State, VNode } from "@stencil/core"; import { createObserver } from "../../utils/observers"; -import { FlowDirection } from "./interfaces"; +import { FlowDirection, FlowItemLikeElement } from "./interfaces"; import { CSS } from "./resources"; import { componentFocusable, @@ -28,7 +28,7 @@ export class Flow implements LoadableComponent { * Removes the currently active `calcite-flow-item`. */ @Method() - async back(): Promise { + async back(): Promise { const { items } = this; const lastItem = items[items.length - 1]; @@ -66,6 +66,19 @@ export class Flow implements LoadableComponent { return activeItem?.setFocus(); } + // -------------------------------------------------------------------------- + // + // Public Properties + // + // -------------------------------------------------------------------------- + + /** + * This property enables the component to consider other custom elements implementing flow-item's interface. + * + * @internal + */ + @Prop() customItemSelectors: string; + // -------------------------------------------------------------------------- // // Private Properties @@ -78,7 +91,7 @@ export class Flow implements LoadableComponent { @State() itemCount = 0; - @State() items: HTMLCalciteFlowItemElement[] = []; + @State() items: FlowItemLikeElement[] = []; itemMutationObserver = createObserver("mutation", () => this.updateFlowProps()); @@ -129,11 +142,13 @@ export class Flow implements LoadableComponent { }; updateFlowProps = (): void => { - const { el, items } = this; + const { customItemSelectors, el, items } = this; - const newItems: HTMLCalciteFlowItemElement[] = Array.from( - el.querySelectorAll("calcite-flow-item") - ).filter((flowItem) => flowItem.closest("calcite-flow") === el) as HTMLCalciteFlowItemElement[]; + const newItems = Array.from( + el.querySelectorAll( + `calcite-flow-item${customItemSelectors ? `,${customItemSelectors}` : ""}` + ) + ).filter((flowItem) => flowItem.closest("calcite-flow") === el); const oldItemCount = items.length; const newItemCount = newItems.length; diff --git a/packages/calcite-components/src/components/flow/interfaces.ts b/packages/calcite-components/src/components/flow/interfaces.ts index 41a9a242584..be33fbfcbb8 100644 --- a/packages/calcite-components/src/components/flow/interfaces.ts +++ b/packages/calcite-components/src/components/flow/interfaces.ts @@ -1 +1,7 @@ export type FlowDirection = "advancing" | "retreating"; + +export type FlowItemLikeElement = Pick< + HTMLCalciteFlowItemElement, + "beforeBack" | "menuOpen" | "setFocus" | "showBackButton" +> & + HTMLElement; diff --git a/packages/calcite-components/src/demos/flow.html b/packages/calcite-components/src/demos/flow.html index 69068fd4ab5..336ffafd7eb 100644 --- a/packages/calcite-components/src/demos/flow.html +++ b/packages/calcite-components/src/demos/flow.html @@ -34,94 +34,93 @@ -
-
basic
+
custom flow-item support
- - - + + + - - -
test
+ + + + +
-
-
+ From 791ebb443745da417c35920a96b29795e1066616 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Sep 2023 00:32:13 +0000 Subject: [PATCH 26/35] chore: release next --- package-lock.json | 8 ++++---- packages/calcite-components-react/CHANGELOG.md | 4 ++++ packages/calcite-components-react/package.json | 4 ++-- packages/calcite-components/CHANGELOG.md | 6 ++++++ packages/calcite-components/package.json | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2abf915fcd4..abe89e72fc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40443,7 +40443,7 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "1.7.0-next.19", + "version": "1.7.0-next.20", "license": "SEE LICENSE.md", "dependencies": { "@floating-ui/dom": "1.5.1", @@ -40470,10 +40470,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "1.7.0-next.19", + "version": "1.7.0-next.20", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^1.7.0-next.19" + "@esri/calcite-components": "^1.7.0-next.20" }, "peerDependencies": { "react": ">=16.7", @@ -42704,7 +42704,7 @@ "@esri/calcite-components-react": { "version": "file:packages/calcite-components-react", "requires": { - "@esri/calcite-components": "^1.7.0-next.19" + "@esri/calcite-components": "^1.7.0-next.20" } }, "@esri/calcite-design-tokens": { diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index a1491976c40..7a7367eccfe 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.20](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.19...@esri/calcite-components-react@1.7.0-next.20) (2023-09-01) + +**Note:** Version bump only for package @esri/calcite-components-react + ## [1.7.0-next.19](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.18...@esri/calcite-components-react@1.7.0-next.19) (2023-08-31) **Note:** Version bump only for package @esri/calcite-components-react diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index 113c3c54cd4..c7b7b1ca299 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,7 +1,7 @@ { "name": "@esri/calcite-components-react", "sideEffects": false, - "version": "1.7.0-next.19", + "version": "1.7.0-next.20", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", @@ -20,7 +20,7 @@ "dist/" ], "dependencies": { - "@esri/calcite-components": "^1.7.0-next.19" + "@esri/calcite-components": "^1.7.0-next.20" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index cc074a7a641..657e8b0d548 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.20](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.19...@esri/calcite-components@1.7.0-next.20) (2023-09-01) + +### Features + +- **flow:** add support for custom flow-item elements ([#7608](https://github.com/Esri/calcite-design-system/issues/7608)) ([197adfe](https://github.com/Esri/calcite-design-system/commit/197adfe763491d728521d4265bc9b2c413be66c9)), closes [#6237](https://github.com/Esri/calcite-design-system/issues/6237) [/github.com/Esri/calcite-design-system/pull/7608/files#diff-82c222ab365cde13a1f1288d936611519dfd9bee1e283164b260ca554c04a191R3-R7](https://github.com/Esri//github.com/Esri/calcite-design-system/pull/7608/files/issues/diff-82c222ab365cde13a1f1288d936611519dfd9bee1e283164b260ca554c04a191R3-R7) [/github.com/Esri/calcite-design-system/pull/7608/files#diff-9b86e64de24dfca441533c63ae0f6834bff10bffbde23fd8bb3989a2259e356cR315-R387](https://github.com/Esri//github.com/Esri/calcite-design-system/pull/7608/files/issues/diff-9b86e64de24dfca441533c63ae0f6834bff10bffbde23fd8bb3989a2259e356cR315-R387) + ## [1.7.0-next.19](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.18...@esri/calcite-components@1.7.0-next.19) (2023-08-31) ### Features diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index a1b05ba0590..f44cac29900 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "1.7.0-next.19", + "version": "1.7.0-next.20", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", From b8bc11ca529e84b40ed40c0bc3dbfa2f0956a7d3 Mon Sep 17 00:00:00 2001 From: Ben Elan Date: Thu, 31 Aug 2023 18:17:03 -0700 Subject: [PATCH 27/35] fix(input, input-number): correctly sanitize numbers when pasting string with 'e' (#7648) **Related Issue:** # ## Summary Prevent error when pasting a string like `123test` into `calcite-input-number` and `calcite-input type="number`. The error was due to our sanitization logic, which splits on `e` for exponential notation and sanitizes each part. In the `123test` example, the rest of the letters after the `e` are sanitized causing a `reduce()` on nothing. On the other hand, `123fine` works fine because the `e` is at the end of the string so the falsy check kicks in before the `reduce()`. The fix uses `join()`, which doesn't throw errors on empty strings. ## Reproduction Sample: https://developers.arcgis.com/calcite-design-system/components/input-number/#sample Steps: 1. Open the sample and paste `123test` into the input 2. Notice `test` isn't sanitized 3. Open the console and you'll see the following error ``` Uncaught TypeError: reduce of empty array with no initial value o https://js.arcgis.com/calcite-components/1.6.1/p-06a878f3.js:6 f https://js.arcgis.com/calcite-components/1.6.1/p-06a878f3.js:6 f https://js.arcgis.com/calcite-components/1.6.1/p-06a878f3.js:6 o https://js.arcgis.com/calcite-components/1.6.1/p-06a878f3.js:6 inputNumberInputHandler https://js.arcgis.com/calcite-components/1.6.1/p-1c7861e8.entry.js:6 ``` --- packages/calcite-components/src/utils/number.spec.ts | 7 +++++++ packages/calcite-components/src/utils/number.ts | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/calcite-components/src/utils/number.spec.ts b/packages/calcite-components/src/utils/number.spec.ts index 16400d1c5aa..62312627f8b 100644 --- a/packages/calcite-components/src/utils/number.spec.ts +++ b/packages/calcite-components/src/utils/number.spec.ts @@ -34,21 +34,28 @@ describe("parseNumberString", () => { expect(parseNumberString("")).toBe(""); expect(parseNumberString("only numbers")).toBe(""); + // eslint-disable-next-line @cspell/spellchecker const lettersAndSymbols = "kjas;lkjwo;aij(*&,asd;flkj-"; + // eslint-disable-next-line @cspell/spellchecker const lettersAndSymbolsWithLeadingNegativeSign = "-ASDF(*^LKJihsdf*&^"; expect(parseNumberString(lettersAndSymbols)).toBe(""); expect(parseNumberString(lettersAndSymbolsWithLeadingNegativeSign)).toBe(""); + expect(parseNumberString("123test")).toBe("123e"); }); it("returns valid number string for string values that compute to a valid number", () => { const stringWithLettersAndDigits = "lkj2323lkj"; const frenchNumber = "1 2345,67"; const positiveInteger = "123345345"; + // eslint-disable-next-line @cspell/spellchecker const stringWithLettersDigitsAndSymbols = "123sdfa34345klndfsi8*&(^asdf5345"; const decimal = "123123.234234"; + // eslint-disable-next-line @cspell/spellchecker const stringWithLettersAndDecimal = "12asdfas$%@$3123.23asdf2a4234"; + // eslint-disable-next-line @cspell/spellchecker const stringWithLettersDecimalAndNonLeadingNegativeSign = "12a-sdfas$%@$3123.23asdf2a4234"; + // eslint-disable-next-line @cspell/spellchecker const stringWithLettersDecimalAndLeadingNegativeSign = "-12a-sdfas$%@$3123.23asdf2a4234"; expect(parseNumberString(stringWithLettersAndDigits)).toBe("2323"); diff --git a/packages/calcite-components/src/utils/number.ts b/packages/calcite-components/src/utils/number.ts index 388b6b0dc34..21d6e1b381c 100644 --- a/packages/calcite-components/src/utils/number.ts +++ b/packages/calcite-components/src/utils/number.ts @@ -120,7 +120,7 @@ export function parseNumberString(numberString?: string): string { } return numberKeys.includes(value); }) - .reduce((string, part) => string + part); + .join(""); return isValidNumber(result) ? new BigDecimal(result).toString() : ""; }); } From afbb764be4789254312b2787aa233616a8752f08 Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Thu, 31 Aug 2023 18:22:04 -0700 Subject: [PATCH 28/35] fix: add drag styles for improved UX (#7644) --- .../calcite-components/src/assets/styles/_sortable.scss | 8 ++++++++ packages/calcite-components/src/assets/styles/global.scss | 2 ++ .../calcite-components/src/assets/styles/includes.scss | 1 + .../calcite-components/src/utils/sortableComponent.ts | 7 +++++++ 4 files changed, 18 insertions(+) create mode 100644 packages/calcite-components/src/assets/styles/_sortable.scss diff --git a/packages/calcite-components/src/assets/styles/_sortable.scss b/packages/calcite-components/src/assets/styles/_sortable.scss new file mode 100644 index 00000000000..a378d82b4da --- /dev/null +++ b/packages/calcite-components/src/assets/styles/_sortable.scss @@ -0,0 +1,8 @@ +@mixin sortable-helper-classes() { + .calcite-sortable--chosen, + .calcite-sortable--ghost, + .calcite-sortable--drag { + @apply border border-dashed; + border-color: var(--calcite-ui-brand); + } +} diff --git a/packages/calcite-components/src/assets/styles/global.scss b/packages/calcite-components/src/assets/styles/global.scss index 023c0f02556..4a2d3f96ca0 100644 --- a/packages/calcite-components/src/assets/styles/global.scss +++ b/packages/calcite-components/src/assets/styles/global.scss @@ -79,3 +79,5 @@ @include animation-reduced-motion(); @include animation-helper-classes(); + +@include sortable-helper-classes(); diff --git a/packages/calcite-components/src/assets/styles/includes.scss b/packages/calcite-components/src/assets/styles/includes.scss index 076d0a7246f..9366044ee72 100644 --- a/packages/calcite-components/src/assets/styles/includes.scss +++ b/packages/calcite-components/src/assets/styles/includes.scss @@ -7,6 +7,7 @@ @import "host"; @import "spacing"; @import "floating-ui"; +@import "sortable"; @mixin slotted($selector, $tag, $scope: "") { #{$scope} slot[name="#{$selector}"]::slotted(#{$tag}), diff --git a/packages/calcite-components/src/utils/sortableComponent.ts b/packages/calcite-components/src/utils/sortableComponent.ts index 143df5c46cf..071d00797bf 100644 --- a/packages/calcite-components/src/utils/sortableComponent.ts +++ b/packages/calcite-components/src/utils/sortableComponent.ts @@ -7,6 +7,12 @@ export interface DragDetail { dragEl: HTMLElement; } +export const CSS = { + ghostClass: "calcite-sortable--ghost", + chosenClass: "calcite-sortable--chosen", + dragClass: "calcite-sortable--drag", +}; + /** * Defines interface for components with sorting functionality. */ @@ -81,6 +87,7 @@ export function connectSortableComponent(component: SortableComponent): void { component.sortable = Sortable.create(component.el, { dataIdAttr, + ...CSS, ...(!!draggable && { draggable }), ...(!!group && { group: { From 1ea52b771d77081a3423c9882f0bb8a94bb13acf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Sep 2023 01:50:02 +0000 Subject: [PATCH 29/35] chore: release next --- package-lock.json | 8 ++++---- packages/calcite-components-react/CHANGELOG.md | 4 ++++ packages/calcite-components-react/package.json | 4 ++-- packages/calcite-components/CHANGELOG.md | 7 +++++++ packages/calcite-components/package.json | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index abe89e72fc3..250b4ac7a86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40443,7 +40443,7 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "1.7.0-next.20", + "version": "1.7.0-next.21", "license": "SEE LICENSE.md", "dependencies": { "@floating-ui/dom": "1.5.1", @@ -40470,10 +40470,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "1.7.0-next.20", + "version": "1.7.0-next.21", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^1.7.0-next.20" + "@esri/calcite-components": "^1.7.0-next.21" }, "peerDependencies": { "react": ">=16.7", @@ -42704,7 +42704,7 @@ "@esri/calcite-components-react": { "version": "file:packages/calcite-components-react", "requires": { - "@esri/calcite-components": "^1.7.0-next.20" + "@esri/calcite-components": "^1.7.0-next.21" } }, "@esri/calcite-design-tokens": { diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index 7a7367eccfe..52957029d9a 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.21](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.20...@esri/calcite-components-react@1.7.0-next.21) (2023-09-01) + +**Note:** Version bump only for package @esri/calcite-components-react + ## [1.7.0-next.20](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@1.7.0-next.19...@esri/calcite-components-react@1.7.0-next.20) (2023-09-01) **Note:** Version bump only for package @esri/calcite-components-react diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index c7b7b1ca299..7ea4d227386 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,7 +1,7 @@ { "name": "@esri/calcite-components-react", "sideEffects": false, - "version": "1.7.0-next.20", + "version": "1.7.0-next.21", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", @@ -20,7 +20,7 @@ "dist/" ], "dependencies": { - "@esri/calcite-components": "^1.7.0-next.20" + "@esri/calcite-components": "^1.7.0-next.21" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index 657e8b0d548..0d42df28e52 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.0-next.21](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.20...@esri/calcite-components@1.7.0-next.21) (2023-09-01) + +### Bug Fixes + +- add drag styles for improved UX ([#7644](https://github.com/Esri/calcite-design-system/issues/7644)) ([afbb764](https://github.com/Esri/calcite-design-system/commit/afbb764be4789254312b2787aa233616a8752f08)) +- **input, input-number:** correctly sanitize numbers when pasting string with 'e' ([#7648](https://github.com/Esri/calcite-design-system/issues/7648)) ([b8bc11c](https://github.com/Esri/calcite-design-system/commit/b8bc11ca529e84b40ed40c0bc3dbfa2f0956a7d3)) + ## [1.7.0-next.20](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@1.7.0-next.19...@esri/calcite-components@1.7.0-next.20) (2023-09-01) ### Features diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index f44cac29900..2c161b00099 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "1.7.0-next.20", + "version": "1.7.0-next.21", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", From 1f44f6b0e05bcaf708015432ef64c8cb660101bd Mon Sep 17 00:00:00 2001 From: JC Franco Date: Thu, 31 Aug 2023 21:25:25 -0700 Subject: [PATCH 30/35] fix(block, block-section): improve a11y (#7557) **Related Issue:** #5565 ## Summary Updated HTML to improve a11y. **Note**: this removes references to outdated `TEXT` constants, which were used before translations were built-in. --- .../block-section/block-section.e2e.ts | 20 +++---- .../block-section/block-section.scss | 13 +++- .../block-section/block-section.tsx | 60 +++++++++---------- .../src/components/block-section/resources.ts | 18 +++--- .../src/components/block/block.e2e.ts | 16 ++--- .../src/components/block/block.tsx | 22 ++----- .../src/components/block/resources.ts | 28 ++++----- 7 files changed, 83 insertions(+), 94 deletions(-) diff --git a/packages/calcite-components/src/components/block-section/block-section.e2e.ts b/packages/calcite-components/src/components/block-section/block-section.e2e.ts index 3ec0ad96f16..5f533869ec4 100644 --- a/packages/calcite-components/src/components/block-section/block-section.e2e.ts +++ b/packages/calcite-components/src/components/block-section/block-section.e2e.ts @@ -1,4 +1,4 @@ -import { CSS, TEXT } from "./resources"; +import { CSS } from "./resources"; import { accessible, defaults, focusable, hidden, reflects, renders, t9n } from "../../tests/commonTests"; import { E2EPage, newE2EPage } from "@stencil/core/testing"; import { html } from "../../../support/formatting"; @@ -87,9 +87,8 @@ describe("calcite-block-section", () => { }); it("can be toggled", async () => { - const page = await newE2EPage({ - html: ``, - }); + const page = await newE2EPage(); + await page.setContent(html``); await assertToggleBehavior(page); }); @@ -119,7 +118,8 @@ describe("calcite-block-section", () => { }); it("can be toggled", async () => { - const page = await newE2EPage({ html: "" }); + const page = await newE2EPage(); + await page.setContent(html``); await assertToggleBehavior(page); }); }); @@ -173,19 +173,19 @@ describe("calcite-block-section", () => { const toggleSpy = await element.spyOnEvent("calciteBlockSectionToggle"); const toggle = await page.find(`calcite-block-section >>> .${CSS.toggle}`); - expect(toggle.getAttribute("aria-label")).toBe(TEXT.expand); + expect(toggle.getAttribute("aria-expanded")).toBe("false"); await toggle.click(); expect(toggleSpy).toHaveReceivedEventTimes(1); expect(await element.getProperty("open")).toBe(true); - expect(toggle.getAttribute("aria-label")).toBe(TEXT.collapse); + expect(toggle.getAttribute("aria-expanded")).toBe("true"); await toggle.click(); expect(toggleSpy).toHaveReceivedEventTimes(2); expect(await element.getProperty("open")).toBe(false); - expect(toggle.getAttribute("aria-label")).toBe(TEXT.expand); + expect(toggle.getAttribute("aria-expanded")).toBe("false"); const keyboardToggleEmitter = toggle.tagName === "CALCITE-ACTION" @@ -206,13 +206,13 @@ describe("calcite-block-section", () => { expect(toggleSpy).toHaveReceivedEventTimes(3); expect(await element.getProperty("open")).toBe(true); - expect(toggle.getAttribute("aria-label")).toBe(TEXT.collapse); + expect(toggle.getAttribute("aria-expanded")).toBe("true"); await keyboardToggleEmitter.press("Enter"); await page.waitForChanges(); expect(toggleSpy).toHaveReceivedEventTimes(4); expect(await element.getProperty("open")).toBe(false); - expect(toggle.getAttribute("aria-label")).toBe(TEXT.expand); + expect(toggle.getAttribute("aria-expanded")).toBe("false"); } }); diff --git a/packages/calcite-components/src/components/block-section/block-section.scss b/packages/calcite-components/src/components/block-section/block-section.scss index 051fde69d8c..3d90065ae6d 100644 --- a/packages/calcite-components/src/components/block-section/block-section.scss +++ b/packages/calcite-components/src/components/block-section/block-section.scss @@ -60,11 +60,18 @@ word-wrap: anywhere; } -.toggle--switch { - calcite-switch { - @apply pointer-events-none; +.toggle--switch-container { + @apply flex items-center relative bg-transparent w-full; + + .focus-guard { + --calcite-label-margin-bottom: 0; + @apply absolute pointer-events-none; + inset-inline-end: 0; margin-inline-start: theme("margin.1"); } +} + +.toggle--switch { .status-icon { margin-inline-start: theme("margin.2"); } diff --git a/packages/calcite-components/src/components/block-section/block-section.tsx b/packages/calcite-components/src/components/block-section/block-section.tsx index a84b1bd2a11..ac76d37b85b 100644 --- a/packages/calcite-components/src/components/block-section/block-section.tsx +++ b/packages/calcite-components/src/components/block-section/block-section.tsx @@ -13,7 +13,6 @@ import { } from "@stencil/core"; import { focusFirstTabbable, getElementDir, toAriaBoolean } from "../../utils/dom"; -import { guid } from "../../utils/guid"; import { isActivationKey } from "../../utils/key"; import { connectLocalized, disconnectLocalized, LocalizedComponent } from "../../utils/locale"; import { @@ -26,7 +25,7 @@ import { import { Status } from "../interfaces"; import { BlockSectionMessages } from "./assets/block-section/t9n"; import { BlockSectionToggleDisplay } from "./interfaces"; -import { CSS, ICONS } from "./resources"; +import { CSS, ICONS, IDS } from "./resources"; import { componentFocusable, LoadableComponent, @@ -117,8 +116,6 @@ export class BlockSection implements LocalizedComponent, T9nComponent, LoadableC @Element() el: HTMLCalciteBlockSectionElement; - private guid = guid(); - @State() effectiveLocale: string; @Watch("effectiveLocale") @@ -213,41 +210,46 @@ export class BlockSection implements LocalizedComponent, T9nComponent, LoadableC const toggleLabel = open ? messages.collapse : messages.expand; - const { guid } = this; - const regionId = `${guid}-region`; - const buttonId = `${guid}-button`; - const headerNode = toggleDisplay === "switch" ? (
-
- {text} +
+
+ {text} +
+ {this.renderStatusIcon()}
- - {this.renderStatusIcon()} + {/* we use calcite-label to use a simple component that will allow us to prevent keyboard focus by setting tabindex="-1" on the host */} + + +
) : (