-
Notifications
You must be signed in to change notification settings - Fork 261
feat: Implement Sana Canvas Text Input, Text Area, Form Field, and Color Picker #3992
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8ad3aff
b3f1b31
bf299e6
cfea945
459c285
f07ad88
fcac97f
bcdef56
37afba8
f7805dd
92518ad
ae9ae1e
6f6ed4b
07db652
9bde3e3
92b234c
646df83
d38721c
4188f45
09245e9
8be501d
4bce17f
7b5ab04
2510d65
3572695
a6e4df6
654bbed
5310fcb
2ba7152
e007095
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import * as React from 'react'; | ||
|
|
||
| import {pickForegroundColor} from '@workday/canvas-kit-react/common'; | ||
| import {cornerShapeStencil, pickForegroundColor} from '@workday/canvas-kit-react/common'; | ||
| import {SystemIcon, systemIconStencil} from '@workday/canvas-kit-react/icon'; | ||
| import {calc, createStencil, handleCsProp, px2rem} from '@workday/canvas-kit-styling'; | ||
| import {checkSmallIcon} from '@workday/canvas-system-icons-web'; | ||
|
|
@@ -12,19 +12,21 @@ export interface ColorSwatchProps extends React.HTMLAttributes<HTMLDivElement> { | |
| } | ||
|
|
||
| export const colorPickerColorSwatchStencil = createStencil({ | ||
| extends: cornerShapeStencil, | ||
| vars: { | ||
| color: '', | ||
| iconColor: '', | ||
| }, | ||
| base: ({color, iconColor}) => ({ | ||
| [systemIconStencil.vars.color]: iconColor, | ||
| [cornerShapeStencil.vars.shape]: system.legacy.shape.sm, | ||
| width: system.legacy.size.xxs, | ||
| height: system.legacy.size.xxs, | ||
| borderRadius: system.legacy.shape.sm, | ||
|
jamesfan marked this conversation as resolved.
|
||
| backgroundColor: color, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Medium: ColorSwatch does not preserve color meaning in Windows High Contrast / forced-colors mode. Since the swatch’s semantic value is its actual color, backgroundColor: color will likely be remapped by forced-colors, causing swatches to appear identical. Consider forcedColorAdjust: 'none' for the swatch color surface, plus a real border/outline fallback for white/selected states since box-shadow is forced to none.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @williamjstanton This should be addressed now. I've added |
||
| display: 'flex', | ||
| alignItems: 'center', | ||
| justifyContent: 'center', | ||
| forcedColorAdjust: 'none', | ||
| '& > *': { | ||
| // Account for 24px icon | ||
| margin: calc.negate(px2rem(2)), | ||
|
|
@@ -34,6 +36,9 @@ export const colorPickerColorSwatchStencil = createStencil({ | |
| withShadow: { | ||
| true: { | ||
| boxShadow: `inset 0px 0px 0px ${px2rem(1)} rgba(0, 0, 0, 0.25)`, | ||
| '@media (forced-colors: active)': { | ||
| outline: `solid ${px2rem(1)} SelectedItemText`, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
|
|
@@ -50,7 +55,7 @@ export const ColorSwatch = ({color, showCheck = false, ...elemProps}: ColorSwatc | |
| colorPickerColorSwatchStencil({ | ||
| color, | ||
| iconColor: pickForegroundColor(color), | ||
| withShadow: showCheck || lowerCasedColor === '#ffffff', | ||
| withShadow: showCheck || lowerCasedColor === '#ffffff' ? 'true' : undefined, | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix for same issue described here. |
||
| }) | ||
| )} | ||
| > | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import {createSubcomponent} from '@workday/canvas-kit-react/common'; | ||
| import {cornerShapeStencil, createSubcomponent} from '@workday/canvas-kit-react/common'; | ||
| import {FlexProps} from '@workday/canvas-kit-react/layout'; | ||
| import {CSProps, calc, createStencil, handleCsProp, px2rem} from '@workday/canvas-kit-styling'; | ||
| import {base, system} from '@workday/canvas-tokens-web'; | ||
|
|
@@ -8,10 +8,11 @@ import {useFormFieldModel} from './hooks'; | |
| export interface FormFieldGroupListProps extends CSProps, FlexProps {} | ||
|
|
||
| const formFieldGroupListStencil = createStencil({ | ||
| extends: cornerShapeStencil, | ||
| base: { | ||
| [cornerShapeStencil.vars.shape]: system.legacy.shape.lg, | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| borderRadius: system.legacy.shape.md, | ||
| gap: system.legacy.gap.sm, | ||
| padding: `${px2rem(10)} ${base.legacy.size150} ${system.legacy.padding.xs}`, | ||
| margin: `0 ${calc.negate(base.legacy.size150)}`, | ||
|
|
@@ -21,12 +22,16 @@ const formFieldGroupListStencil = createStencil({ | |
| modifiers: { | ||
| error: { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Medium: Error/caution visual affordances rely heavily on boxShadow, which is removed in forced-colors mode. For TextInput, TextArea inherits this behavior; for grouped fields, the group ring is entirely shadow-based. Add @media (forced-colors: active) fallbacks using real border/outline and system colors so Windows High Contrast users can still perceive invalid/caution states.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @williamjstanton I'm consulting the MDN docs for system colors. Which color would you recommend I use for error and caution states? For example, ActiveText is visually similar to our standard error color (red), but doesn't line up semantically (i.e., the form field error has nothing to do with the "text of active links").
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @williamjstanton Also, it wasn't quite clear from your original comment, but is it ok to leave TextInput and TextArea as-is given they both still have a border in the error and caution states?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is a really good question, and I don't know. There isn't any semantically correct option available at all. The recommendation is to use border differentiation like
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm using I can double the thickness, however. Out of curiosity, what's the rationale behind differentiating the border if forced-colors is active? Also, did you mean to use
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Really? Presents as black? I'd assumed it would present as white on a dark background. I'll definitely need to test on a real Windows machine. Rationale: Since we can't use color to differentiate a border on an input that already uses a border in a valid state (e.g. TextInput), that's where it is useful to use more thickness or a I don't think that rationale applies in this situation with the group list. (Because the border / outline / boxShadow is absent in valid state.)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @williamjstanton Apologies, I was confused. I didn't realize Windows High Contrast mode was an example of a forced colors mode (I thought the two could be toggled independently of one another. Appreciate the explanation. The original issue should be resolved:
|
||
| error: { | ||
| backgroundColor: system.legacy.color.brand.surface.critical.default, | ||
| boxShadow: `inset 0 0 0 ${px2rem(2)} ${system.legacy.color.brand.border.critical}`, | ||
| '@media (forced-colors: active)': { | ||
| outline: `solid ${px2rem(2)} ButtonBorder`, | ||
| }, | ||
| }, | ||
| caution: { | ||
| backgroundColor: system.legacy.color.brand.surface.caution.default, | ||
| boxShadow: `inset 0 0 0 ${px2rem(1)} ${system.legacy.color.brand.border.caution}, inset 0 0 0 ${px2rem(3)} ${system.legacy.color.brand.focus.caution.inner}`, | ||
| '@media (forced-colors: active)': { | ||
| outline: `solid ${px2rem(2)} ButtonBorder`, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,9 @@ | ||
| import {ErrorType, GrowthBehavior, createComponent} from '@workday/canvas-kit-react/common'; | ||
| import { | ||
| ErrorType, | ||
| GrowthBehavior, | ||
| cornerShapeStencil, | ||
| createComponent, | ||
| } from '@workday/canvas-kit-react/common'; | ||
| import {mergeStyles} from '@workday/canvas-kit-react/layout'; | ||
| import {CSProps, createStencil, cssVar, px2rem} from '@workday/canvas-kit-styling'; | ||
| import {system} from '@workday/canvas-tokens-web'; | ||
|
|
@@ -15,10 +20,12 @@ export interface TextInputProps extends GrowthBehavior, CSProps { | |
| } | ||
|
|
||
| export const textInputStencil = createStencil({ | ||
| extends: cornerShapeStencil, | ||
| vars: { | ||
| width: '', | ||
| }, | ||
| base: ({width}) => ({ | ||
| [cornerShapeStencil.vars.shape]: system.legacy.shape.lg, | ||
| fontFamily: system.fontFamily.default, | ||
| fontSize: system.legacy.fontSize.subtext.lg, | ||
| fontWeight: system.fontWeight.normal, | ||
|
|
@@ -27,7 +34,6 @@ export const textInputStencil = createStencil({ | |
| display: 'block', | ||
| border: `${px2rem(1)} solid ${system.color.border.input.default}`, | ||
| backgroundColor: system.legacy.color.surface.default, | ||
| borderRadius: system.legacy.shape.md, | ||
| height: system.legacy.size.md, | ||
| transition: '0.2s box-shadow, 0.2s border-color', | ||
| padding: system.legacy.padding.xs, // Compensate for border | ||
|
|
@@ -74,7 +80,6 @@ export const textInputStencil = createStencil({ | |
| borderColor: system.legacy.color.brand.border.critical, | ||
| // borderWidth: px2rem(2), | ||
| boxShadow: `inset 0 0 0 ${px2rem(2)} ${system.legacy.color.brand.border.critical}`, | ||
| backgroundColor: system.legacy.color.brand.surface.critical.default, | ||
| '&:is(:hover, .hover, :disabled, .disabled, :focus-visible:not([disabled]), .focus:not([disabled]))': | ||
| { | ||
| borderColor: system.legacy.color.brand.border.critical, | ||
|
|
@@ -85,11 +90,17 @@ export const textInputStencil = createStencil({ | |
| 0 0 0 4px ${system.legacy.color.brand.border.primary}`, | ||
| outlineOffset: px2rem(2), | ||
| }, | ||
| // For Windows High Contrast mode: display error outline at double the | ||
| // width of the focus ring outline (to differentiate the error outline | ||
| // from the focus ring outline since both outlines will be the same | ||
| // color in WHC mode). | ||
| '@media (forced-colors: active)': { | ||
| outline: `solid ${px2rem(4)} ButtonBorder`, | ||
| }, | ||
| }, | ||
| caution: { | ||
| borderColor: system.legacy.color.brand.border.caution, | ||
| boxShadow: `inset 0 0 0 ${px2rem(2)} ${system.legacy.color.brand.focus.caution.inner}`, | ||
| backgroundColor: system.legacy.color.brand.surface.caution.default, | ||
| '&:is(:hover, .hover, :disabled, .disabled, :focus-visible:not([disabled]), .focus:not([disabled]))': | ||
| { | ||
| borderColor: system.legacy.color.brand.border.caution, | ||
|
|
@@ -100,6 +111,15 @@ export const textInputStencil = createStencil({ | |
| 0 0 0 4px ${system.legacy.color.brand.border.primary}`, | ||
| }, | ||
| outlineOffset: px2rem(2), | ||
| // For Windows High Contrast mode: see comment above regarding the | ||
| // error outline. Unlike the error state, outlineOffset for the | ||
| // caution state is defined outside of the `(:focus-visible, .focus)` | ||
| // selector so we must override it here to prevent the caution outline | ||
| // from being separated too far from the input. | ||
| '@media (forced-colors: active)': { | ||
| outline: `solid ${px2rem(4)} ButtonBorder`, | ||
| outlineOffset: 0, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
|
|
@@ -117,7 +137,11 @@ export const TextInput = createComponent('input')({ | |
| ref={ref} | ||
| {...mergeStyles( | ||
| elemProps, | ||
| textInputStencil({width: typeof width === 'number' ? px2rem(width) : width, grow, error}) | ||
| textInputStencil({ | ||
| width: typeof width === 'number' ? px2rem(width) : width, | ||
| grow: grow === true ? 'true' : grow === false ? 'false' : undefined, | ||
| error, | ||
| }) | ||
|
Comment on lines
+140
to
+144
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. connected to #3896, I'll talk to Alan about that the next week
Comment on lines
+140
to
+144
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need to fix that issue finally :D I'll talk to Alan about that when he is back |
||
| )} | ||
| /> | ||
| ); | ||
|
|
||



Uh oh!
There was an error while loading. Please reload this page.