From 3818c72bc176cb4f5592c99a92355895f1ef95e3 Mon Sep 17 00:00:00 2001 From: Stuart Hendren Date: Wed, 18 May 2022 17:05:18 +0000 Subject: [PATCH] feat(input): adds limited support for invalid and valid pesudo-class Adds the correct styling for invalid and valid pesudo props. This using the :has selector in places which has limitted support. fix #269 --- .../FormControl/FormControl.stories.tsx | 67 +++++++++++++++++++ src/components/Input/inputStyles.ts | 39 +++++++++++ src/docs/examples/form.stories.tsx | 3 +- 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/src/components/FormControl/FormControl.stories.tsx b/src/components/FormControl/FormControl.stories.tsx index e438b2d9..278e30d8 100644 --- a/src/components/FormControl/FormControl.stories.tsx +++ b/src/components/FormControl/FormControl.stories.tsx @@ -41,6 +41,7 @@ export const API_1: Story = () => { const [disabled, setDisabled] = useState(false) const [error, setError] = useState(false) const [valid, setValid] = useState(false) + const [native, setNative] = useState(false) const rotate = rotateCheckedState(setRequired) @@ -67,6 +68,11 @@ export const API_1: Story = () => { onCheckedChange={setValid} label="Valid" > + @@ -77,6 +83,7 @@ export const API_1: Story = () => { error={error} valid={valid} disabled={disabled} + native={native} required={required === 'indeterminate' ? undefined : required} /> { ) } +/** + * The `native` prop can be added to allow the use of the native html `:invalid` and `:valid` pseudo-classes. + * + * This is not the default as can have negative user experience when required forms show initial error. + * + * This is useful for styling the inputs when using the native html validation attributes. + * The is added for checkbox, radio, and select inputs too but uses the :has selector which has limited support. + */ +export const Native: Story = () => { + return ( +
+ + + + + + + + + + + ) +} + +/** + * Behavioural test/demo. Use the checkboxes to add these properties to all form elements. + */ export const Controls: Story = () => { const [required, setRequired] = useState('indeterminate') const [disabled, setDisabled] = useState(false) const [error, setError] = useState(false) const [valid, setValid] = useState(false) + const [native, setNative] = useState(false) const rotate = rotateCheckedState(setRequired) @@ -212,6 +263,7 @@ export const Controls: Story = () => { valid, disabled, required: required === 'indeterminate' ? undefined : required, + native, } return ( @@ -237,6 +289,11 @@ export const Controls: Story = () => { onCheckedChange={setValid} label="Valid" > +
@@ -309,11 +366,15 @@ export const Controls: Story = () => { ) } +/** + * Behavioural test/demo with ids, to ensure elements are correctly referenced in aria. Use the checkboxes to add these properties to all form elements. + */ export const WithIds: Story = () => { const [required, setRequired] = useState('indeterminate') const [disabled, setDisabled] = useState(false) const [error, setError] = useState(false) const [valid, setValid] = useState(false) + const [native, setNative] = useState(false) const rotate = rotateCheckedState(setRequired) @@ -322,6 +383,7 @@ export const WithIds: Story = () => { valid, disabled, required: required === 'indeterminate' ? undefined : required, + native, } return ( @@ -347,6 +409,11 @@ export const WithIds: Story = () => { onCheckedChange={setValid} label="Valid" > + diff --git a/src/components/Input/inputStyles.ts b/src/components/Input/inputStyles.ts index beefb292..9ab71816 100644 --- a/src/components/Input/inputStyles.ts +++ b/src/components/Input/inputStyles.ts @@ -117,6 +117,29 @@ export const inputStyles = css({ $$active: '$colors$success', }, }, + native: { + false: {}, + true: { + '&:invalid': { + backgroundColor: '$errorBackground', + $$inactive: '$colors$errorLowlight', + $$active: '$colors$error', + }, + '&:valid': { + $$inactive: '$colors$successLowlight', + $$active: '$colors$success', + }, + '&:has(+select:invalid)': { + backgroundColor: '$errorBackground', + $$inactive: '$colors$errorLowlight', + $$active: '$colors$error', + }, + '&:has(+select:valid)': { + $$inactive: '$colors$successLowlight', + $$active: '$colors$success', + }, + }, + }, cursor: { default: { cursor: 'default', @@ -223,6 +246,22 @@ export const checkStyles = css({ $$active: '$colors$success', }, }, + native: { + false: {}, + true: { + '&:has(+input:invalid)': { + backgroundColor: '$errorBackground', + borderColor: '$colors$error', + $$inactive: '$colors$errorLowlight', + $$active: '$colors$error', + }, + '&:has(+input:valid)': { + borderColor: '$colors$success', + $$inactive: '$colors$successLowlight', + $$active: '$colors$success', + }, + }, + }, destructive: { true: { $$main: '$colors$error', diff --git a/src/docs/examples/form.stories.tsx b/src/docs/examples/form.stories.tsx index 1e1de8c8..352be096 100644 --- a/src/docs/examples/form.stories.tsx +++ b/src/docs/examples/form.stories.tsx @@ -66,6 +66,7 @@ export const Form: Story = () => { id="name" label="Name" name="name" + required onValueChange={(value) => setName(value)} value={name} /> @@ -108,7 +109,7 @@ export const Form: Story = () => { checked={notifyBrowser} /> Please Specify your role: - setRole(v)}> + setRole(v)}>