diff --git a/packages/calcite-components/src/components/text-area/resources.ts b/packages/calcite-components/src/components/text-area/resources.ts index 82962d86fab..965d6d7bb39 100644 --- a/packages/calcite-components/src/components/text-area/resources.ts +++ b/packages/calcite-components/src/components/text-area/resources.ts @@ -11,10 +11,10 @@ export const CSS = { readOnly: "readonly", textAreaInvalid: "text-area--invalid", footerSlotted: "footer--slotted", - borderColor: "border--color", hide: "hide", - blockSizeFull: "block-size--full", footerEndSlotOnly: "footer--end-only", + textArea: "text-area", + textAreaOnly: "text-area--only", }; export const IDS = { diff --git a/packages/calcite-components/src/components/text-area/text-area.e2e.ts b/packages/calcite-components/src/components/text-area/text-area.e2e.ts index 731ecb95f2c..6af572942a7 100644 --- a/packages/calcite-components/src/components/text-area/text-area.e2e.ts +++ b/packages/calcite-components/src/components/text-area/text-area.e2e.ts @@ -10,6 +10,7 @@ import { reflects, renders, t9n, + themed, } from "../../tests/commonTests"; import { html } from "../../../support/formatting"; import { CSS } from "./resources"; @@ -201,4 +202,83 @@ describe("calcite-text-area", () => { describe("translation support", () => { t9n("calcite-text-area"); }); + + describe("theme", () => { + describe("default", () => { + themed(html``, { + "--calcite-text-area-background-color": [ + { + shadowSelector: `.${CSS.textArea}`, + targetProp: "backgroundColor", + }, + { + shadowSelector: `.${CSS.footer}`, + targetProp: "backgroundColor", + }, + ], + "--calcite-text-area-border-color": { + shadowSelector: `.${CSS.textArea}`, + targetProp: "borderColor", + }, + "--calcite-text-area-font-size": [ + { + shadowSelector: `.${CSS.textArea}`, + targetProp: "fontSize", + }, + { + shadowSelector: `.${CSS.footer}`, + targetProp: "fontSize", + }, + ], + "--calcite-text-area-max-height": { + shadowSelector: `.${CSS.textArea}`, + targetProp: "maxHeight", + }, + "--calcite-text-area-min-height": { + shadowSelector: `.${CSS.textArea}`, + targetProp: "minHeight", + }, + "--calcite-text-area-text-color": { + shadowSelector: `.${CSS.textArea}`, + targetProp: "color", + }, + "--calcite-text-area-placeholder-text-color": { + shadowSelector: `.${CSS.textArea}::placeholder`, + targetProp: "color", + }, + }); + }); + + describe("max-chars", () => { + themed(html``, { + "--calcite-text-area-divider-color": { + shadowSelector: `.${CSS.textArea}`, + targetProp: "borderBlockEndColor", + }, + "--calcite-text-area-footer-border-color": [ + { + shadowSelector: `.${CSS.footer}`, + targetProp: "borderBottomColor", + }, + { + shadowSelector: `.${CSS.footer}`, + targetProp: "borderLeftColor", + }, + { + shadowSelector: `.${CSS.footer}`, + targetProp: "borderRightColor", + }, + ], + }); + }); + + describe("over limit", () => { + themed(html``, { + "--calcite-text-area-character-limit-text-color": { + shadowSelector: `.${CSS.characterLimit}`, + targetProp: "color", + }, + }); + }); + }); }); diff --git a/packages/calcite-components/src/components/text-area/text-area.scss b/packages/calcite-components/src/components/text-area/text-area.scss index 9069e11dd0f..77c8cf070f0 100644 --- a/packages/calcite-components/src/components/text-area/text-area.scss +++ b/packages/calcite-components/src/components/text-area/text-area.scss @@ -1,182 +1,211 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-text-area-background-color: Specifies the background color of the component. + * @prop --calcite-text-area-border-color: Specifies the border color of the text area. + * @prop --calcite-text-area-character-limit-text-color: Specifies the color of the character limit text displayed in footer of the component. + * @prop --calcite-text-area-divider-color: Specifies the color of the divider between the text area and footer. + * @prop --calcite-text-area-font-size: Specifies the font size of the thext area and footer. + * @prop --calcite-text-area-max-height: Specifies the the maximum height of the text area in the component. + * @prop --calcite-text-area-min-height: Specifies the minimum height of the text area in the component. + * @prop --calcite-text-area-max-width: Specifies the the maximum width of the text area in the component. + * @prop --calcite-text-area-min-width: Specifies the minimum width of the text area in the component. + * @prop --calcite-text-area-text-color: Specifies the color of text in the component. + * @prop --calcite-text-area-footer-border-color: Specifies the border color of the footer. + */ + :host { - @apply inline-block - relative - w-full - h-full; -} - -textarea { - @apply font-sans - text-color-1 - relative - block - bg-foreground-1 - box-border - border-color-input - border - w-full - m-0; - min-inline-size: theme("spacing.48"); - border-block-end: 1px solid var(--calcite-color-border-3); + @apply inline-block relative w-full h-full; + + --calcite-internal-text-area-border-color: var(--calcite-text-area-border-color, var(--calcite-color-border-input)); + --calcite-internal-text-area-footer-border-color: var( + --calcite-text-area-footer-border-color, + var(--calcite-internal-text-area-border-color) + ); +} + +.text-area, +.footer { + font-size: var(--calcite-text-area-font-size, var(--calcite-font-size--1)); + background-color: var(--calcite-text-area-background-color, var(--calcite-color-foreground-1)); + padding-block: var(--calcite-spacing-sm); + padding-inline: var(--calcite-spacing-md); +} + +.text-area { + @apply relative font-sans block box-border w-full m-0; + + --calcite-internal-text-area-border-block-end-color: var(--calcite-internal-text-area-border-color); + + border: var(--calcite-border-width-sm) solid var(--calcite-internal-text-area-border-color); + border-block-end-color: var(--calcite-internal-text-area-border-block-end-color); + color: var(--calcite-text-area-text-color, var(--calcite-color-text-1)); + font-family: var(--calcite-sans-family); + + max-block-size: var(--calcite-text-area-max-height); + min-block-size: var(--calcite-text-area-min-height); + max-inline-size: var(--calcite-text-area-max-width); + min-inline-size: var(--calcite-text-area-min-width, theme("spacing.48")); + + &::placeholder { + @apply font-normal; + } + @media screen and (max-width: 480px) { @apply resize-none; } + &:focus { @apply focus-inset; } + &.text-area--invalid { - @apply border-color-danger - border - border-solid - border-b; + --calcite-internal-text-area-border-color: var( + --calcite-text-area-border-color, + var(--calcite-color-status-danger) + ); + &:focus { @apply focus-inset-danger; } } + &.footer--slotted { min-inline-size: theme("spacing.72"); } + + &:not(.text-area--only, .text-area--invalid) { + --calcite-internal-text-area-border-block-end-color: var( + --calcite-text-area-divider-color, + var(--calcite-color-border-3) + ); + } } .footer { - @apply flex - bg-foreground-1 - box-border - border-color-input - border - border-solid - border-t-0 - items-center; + @apply flex box-border items-center; + border: var(--calcite-border-width-sm) solid var(--calcite-internal-text-area-footer-border-color); + border-block-start: var(--calcite-border-width-none); + min-block-size: 2.25rem; } .character-limit { - @apply flex - justify-end - text-color-2 - items-center - font-normal - whitespace-nowrap; + @apply flex justify-end items-center whitespace-nowrap; + + font-size: var(--calcite-text-area-font-size, var(--calcite-font-size--1)); + font-weight: var(--calcite-font-weight-regular); + color: var(--calcite-text-area-character-limit-text-color, var(--calcite-color-text-2)); + padding-inline-start: var(--calcite-spacing-md); } .character--over-limit { - @apply font-bold - text-danger; + font-weight: var(--calcite-font-weight-bold); + color: var(--calcite-color-status-danger); +} + +.readonly { + background-color: var(--calcite-color-background); + font-weight: var(--calcite-font-weight-medium); +} + +.content, +.hide { + @apply hidden; +} + +.container { + @apply flex justify-between w-full; +} + +.footer--end-only { + @apply justify-end; +} + +.assistive-text { + @apply sr-only; +} + +.text-area.text-area--only { + @apply h-full; } -:host([resize="none"]) textarea { +:host([resize="none"]) .text-area { @apply resize-none; } -:host([resize="horizontal"]) textarea { +:host([resize="horizontal"]) .text-area { @apply resize-x; } -:host([resize="vertical"]) textarea { +:host([resize="vertical"]) .text-area { @apply resize-y; } :host([scale="s"]) { - @apply text-n2; + .text-area, + .footer, + .character-limit { + @apply pl-2; + font-size: var(--calcite-text-area-font-size, var(--calcite-font-size--2)); + } + .footer { - @apply py-1 - px-2; min-block-size: 1.75rem; } - textarea { - @apply py-1 - px-2; - } - textarea, - .footer, - .character-limit { - @apply text-n2 - pl-2; + .text-area { + @apply py-1 px-2; } } :host([scale="m"]) { - textarea { - @apply py-2 - px-3; + .text-area { + @apply py-2 px-3; } + .footer { - @apply py-2 - px-3; + @apply py-2 px-3; min-block-size: 2.25rem; } - - textarea, - .footer, - .character-limit { - @apply text-n1 - pl-3; - } } :host([scale="l"]) { - @apply text-0; - textarea { - @apply py-3 - px-4; + .text-area, + .footer { + font-size: var(--calcite-text-area-font-size, var(--calcite-font-size-0)); + padding-block: var(--calcite-spacing-md); + padding-inline: var(--calcite-spacing-xl); } + .footer { - @apply py-3 - px-4; min-block-size: 2.75rem; } - textarea, + .text-area, .footer, .character-limit { - @apply text-0 - pl-4; + font-size: var(--calcite-text-area-font-size, var(--calcite-font-size-0)); + padding-inline-start: var(--calcite-spacing-xl); } } :host([status="invalid"]) { - textarea { - @apply border-color-danger; - } - textarea:focus { + --calcite-internal-text-area-border-color: var(--calcite-text-area-border-color, var(--calcite-color-status-danger)); + + .text-area:focus { @apply focus-inset-danger; } } -.readonly { - @apply bg-background font-medium; -} - -.border--color { - @apply border-b border-color-input; - &:focus { - @apply border-b-2; +:host([disabled]) { + .text-area, + .footer { + opacity: var(--calcite-opacity-half); } } -textarea.block-size--full { - @apply h-full; -} - -.content, -.hide { - @apply hidden; -} - -.container { - @apply flex - justify-between - w-full; -} - -.footer--end-only { - @apply justify-end; -} - -.assistive-text { - @apply sr-only; -} - @include form-validation-message(); @include hidden-form-input(); @include disabled(); diff --git a/packages/calcite-components/src/components/text-area/text-area.tsx b/packages/calcite-components/src/components/text-area/text-area.tsx index b7c8a339e5e..e9562989af4 100644 --- a/packages/calcite-components/src/components/text-area/text-area.tsx +++ b/packages/calcite-components/src/components/text-area/text-area.tsx @@ -313,11 +313,11 @@ export class TextArea aria-label={getLabelText(this)} autofocus={this.el.autofocus} class={{ + [CSS.textArea]: true, [CSS.readOnly]: this.readOnly, [CSS.textAreaInvalid]: this.isCharacterLimitExceeded(), [CSS.footerSlotted]: this.endSlotHasElements && this.startSlotHasElements, - [CSS.blockSizeFull]: !hasFooter, - [CSS.borderColor]: !hasFooter, + [CSS.textAreaOnly]: !hasFooter, }} cols={this.columns} disabled={this.disabled} diff --git a/packages/calcite-components/src/custom-theme.stories.ts b/packages/calcite-components/src/custom-theme.stories.ts index a4256c15a51..7aedb7c6c57 100644 --- a/packages/calcite-components/src/custom-theme.stories.ts +++ b/packages/calcite-components/src/custom-theme.stories.ts @@ -31,6 +31,7 @@ import { progress, progressTokens } from "./custom-theme/progress"; import { segmentedControl } from "./custom-theme/segmented-control"; import { slider } from "./custom-theme/slider"; import { tabs } from "./custom-theme/tabs"; +import { textArea, textAreaTokens } from "./custom-theme/text-area"; import { avatarIcon, avatarInitials, avatarThumbnail, avatarTokens } from "./custom-theme/avatar"; const globalTokens = { @@ -113,7 +114,7 @@ const kitchenSink = (args: Record, useTestValues = false) => ${chips} ${pagination} ${slider} -
${datePicker} ${tabs} ${loader} ${calciteSwitch} ${avatarIcon} ${avatarInitials} ${avatarThumbnail} ${progress} ${handle}
+
${datePicker} ${tabs} ${loader} ${calciteSwitch} ${avatarIcon} ${avatarInitials} ${avatarThumbnail} ${progress} ${handle} ${textArea}
@@ -137,6 +138,7 @@ export default { ...handleTokens, ...progressTokens, ...inputTokens, + ...textAreaTokens, }, }; @@ -161,6 +163,7 @@ export const theming_TestOnly = (): string => { ...handleTokens, ...progressTokens, ...inputTokens, + ...textAreaTokens, }, true, ); diff --git a/packages/calcite-components/src/custom-theme/text-area.ts b/packages/calcite-components/src/custom-theme/text-area.ts new file mode 100644 index 00000000000..b29df775832 --- /dev/null +++ b/packages/calcite-components/src/custom-theme/text-area.ts @@ -0,0 +1,17 @@ +import { html } from "../../support/formatting"; + +export const textAreaTokens = { + calciteTextAreaBackgroundColor: "", + calciteTextAreaBorderColor: "", + calciteTextAreaCharacterLimitTextColor: "", + calciteTextAreaDividerColor: "", + calciteTextAreaFontSize: "", + calciteTextAreaFooterBorderColor: "", + calciteTextAreaMaxHeight: "", + calciteTextAreaMinHeight: "", + calciteTextAreaMaxWidth: "", + calciteTextAreaMinWidth: "", + calciteTextAreaTextColor: "", +}; + +export const textArea = html``; diff --git a/packages/calcite-components/src/demos/text-area.html b/packages/calcite-components/src/demos/text-area.html index f32e4f5165c..3eba8e74515 100644 --- a/packages/calcite-components/src/demos/text-area.html +++ b/packages/calcite-components/src/demos/text-area.html @@ -6,6 +6,20 @@ TextArea