Skip to content
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

Fix a11y form component ids #2599

Merged
merged 53 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
5fcc7b0
fix: a11y issue for all form components
nmerget Apr 30, 2024
3514dd4
Merge branch 'main' of github.com:db-ui/mono into fix-a11y-form-compo…
nmerget Apr 30, 2024
51c14bf
fix: a11y issue for switch
nmerget Apr 30, 2024
dabcf94
fix: issue from PR
nmerget May 8, 2024
2b04630
Merge branch 'main' into fix-a11y-form-component-ids
nmerget May 8, 2024
28d00c9
fix: issue with vue
nmerget May 8, 2024
fa45de4
Merge remote-tracking branch 'origin/fix-a11y-form-component-ids' int…
nmerget May 8, 2024
55ddd0d
Merge branch 'main' into fix-a11y-form-component-ids
mfranzke May 10, 2024
91b2e60
Update packages/components/src/components/select/select.lite.tsx
nmerget May 10, 2024
ded12f9
Update checkbox.lite.tsx
mfranzke May 10, 2024
286eae3
Update input.lite.tsx
mfranzke May 10, 2024
524e892
Update textarea.lite.tsx
mfranzke May 10, 2024
4ad533c
Update input.lite.tsx
mfranzke May 10, 2024
613e971
fix: issues from PR
nmerget May 10, 2024
2f92ef4
Merge remote-tracking branch 'origin/fix-a11y-form-component-ids' int…
nmerget May 10, 2024
ad99509
Merge branch 'main' into fix-a11y-form-component-ids
nmerget May 10, 2024
99da69a
Merge branch 'main' into fix-a11y-form-component-ids
nmerget May 10, 2024
315e2e0
fix: issues with form-components
nmerget May 10, 2024
ea3abae
fix: removed unused dependency
nmerget May 10, 2024
3fd2d65
feat: add new tests for form-components for react- and vue-showcase
nmerget May 13, 2024
809fd76
Merge branch 'main' of github.com:db-ui/mono into fix-a11y-form-compo…
nmerget May 13, 2024
ce2d6a1
feat: add new tests for form-components for angular-showcase
nmerget May 13, 2024
54529cc
Merge branch 'main' into fix-a11y-form-component-ids
nmerget May 13, 2024
8749d59
Merge branch 'main' into fix-a11y-form-component-ids
nmerget May 13, 2024
70f4b76
fix: issue from PR
nmerget May 14, 2024
bb1987e
chore: update from main
nmerget May 14, 2024
309091f
Merge branch 'main' of github.com:db-ui/mono into fix-a11y-form-compo…
nmerget May 15, 2024
072bb2b
fix: issue with angular
nmerget May 15, 2024
a641c27
Merge branch 'main' into fix-a11y-form-component-ids
nmerget May 21, 2024
484ed3a
Merge branch 'main' into fix-a11y-form-component-ids
mfranzke May 30, 2024
dff6ca5
Merge branch 'main' of github.com:db-ui/mono into fix-a11y-form-compo…
nmerget Jun 26, 2024
09f1f23
chore: update from main
nmerget Jun 26, 2024
c823486
fix: issue with vue showcase
nmerget Jun 26, 2024
91f7074
Merge branch 'main' of github.com:db-ui/mono into fix-a11y-form-compo…
nmerget Jun 27, 2024
6abdad7
fix: issue with vue refs
nmerget Jun 27, 2024
5031097
chore: add comments for overwrites
nmerget Jul 5, 2024
9402d66
Merge branch 'main' into fix-a11y-form-component-ids
nmerget Jul 5, 2024
a7cc66f
fix: issues from PR
nmerget Jul 5, 2024
a4dcc9a
Merge branch 'main' into fix-a11y-form-component-ids
mfranzke Jul 8, 2024
754a576
Merge branch 'main' into fix-a11y-form-component-ids
mfranzke Jul 8, 2024
f6e6ab2
Merge branch 'main' into fix-a11y-form-component-ids
nmerget Jul 9, 2024
a3ba11c
Update packages/components/src/shared/constants.ts
nmerget Jul 9, 2024
e1ef8a0
Update showcases/angular-showcase/src/app/components/form/checkboxes/…
nmerget Jul 9, 2024
21dc640
Update showcases/angular-showcase/src/app/components/form/selects/sel…
nmerget Jul 9, 2024
280a468
Update showcases/angular-showcase/src/app/components/form/inputs/inpu…
nmerget Jul 9, 2024
051f70e
Update showcases/angular-showcase/src/app/components/form/textareas/t…
nmerget Jul 9, 2024
df1a288
Merge branch 'main' into fix-a11y-form-component-ids
mfranzke Jul 9, 2024
a72b6e2
Update showcases/angular-showcase/src/app/components/form/textareas/t…
nmerget Jul 10, 2024
3668211
Update showcases/angular-showcase/src/app/components/form/textareas/t…
nmerget Jul 10, 2024
6d18f0f
Update showcases/angular-showcase/src/app/components/form/textareas/t…
nmerget Jul 10, 2024
d6b3354
Update showcases/vue-showcase/src/components/form/Textareas.vue
nmerget Jul 10, 2024
71e30b7
chore: update from main
nmerget Jul 10, 2024
5d94ff2
Merge remote-tracking branch 'origin/fix-a11y-form-component-ids' int…
nmerget Jul 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .xo-config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ module.exports = {
'@typescript-eslint/no-unsafe-return': 0, // valid for app
'import/no-extraneous-dependencies': 0 // foundation and component.css are inside this repo
}
},
{
files: ['./**/*.spec.ts'],
rules: {
// Playwright tests are async we shall use loops there
'no-await-in-loop': 0
}
}
],
rules: {
Expand Down
8 changes: 8 additions & 0 deletions packages/components/scripts/post-build/angular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ const changeFile = (input: string) => {
return line.replace(': ElementRef', ': ElementRef | undefined');
}

// We need to remove "nativeElement" in template part, because it only exists in ts
if (
line.includes('ref.nativeElement') &&
(line.includes('{{') || line.includes('}}'))
) {
return line.replace('.nativeElement', '');
}

if (line.includes('.nativeElement') && !line.includes('=')) {
return line.replace('.nativeElement', '?.nativeElement');
}
Expand Down
11 changes: 11 additions & 0 deletions packages/components/scripts/post-build/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ export const getComponents = (): Component[] => [
{
from: 'scrollContainer = null;',
to: 'scrollContainer: Element | null = null;'
},
{
from: '& > .db-tab-panel',
to: '& > dbtabpanel > .db-tab-panel, & > db-tab-panel > .db-tab-panel'
}
]
}
Expand Down Expand Up @@ -149,6 +153,13 @@ export const getComponents = (): Component[] => [

{
name: 'select',
overwrites: {
react: [
// React not allowing selected for options
{ from: 'selected={option.selected}', to: '' },
{ from: 'selected={optgroupOption.selected}', to: '' }
]
},
config: {
vue: {
vModel: [{ modelValue: 'value', binding: ':value' }]
Expand Down
6 changes: 6 additions & 0 deletions packages/components/scripts/post-build/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ export default (tmp?: boolean) => {
`{...filterPassingProps(props,${JSON.stringify(
component?.config?.react?.propsPassingFilter ?? []
)})}`
},
/* We need to overwrite the internal state._value property just for react to have controlled components.
* It works for Angular & Vue, so we overwrite it only for React. */
{
from: 'props.value ?? _value',
to: 'props.value'
nmerget marked this conversation as resolved.
Show resolved Hide resolved
}
];

Expand Down
21 changes: 21 additions & 0 deletions packages/components/scripts/post-build/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,30 @@ export default (tmp?: boolean) => {
{
from: /immediate: true,/g,
to: 'immediate: true,\nflush: "post"'
},
/* `this` can be undefined for ssr (nuxt) we need to add */
{
from: /this.\$refs.ref\?.validationMessage/g,
to: 'this?.$refs.ref?.validationMessage'
nmerget marked this conversation as resolved.
Show resolved Hide resolved
}
];

/* This is a workaround for valid/invalid Messages.
* If a valid/invalid message appears it will use the old this._value,
* so we need to overwrite this._value with the current event.target.value. */
[
'HTMLSelectElement',
'HTMLInputElement',
'HTMLTextAreaElement'
].forEach((element) => {
replacements.push({
from: `handleInput(event: InputEvent<${element}>) {`,
to:
`handleInput(event: InputEvent<${element}>) {\n` +
'this._value = (event.target as any).value;'
});
});

replaceInFileSync({
files: vueFile,
processor(input) {
Expand Down
75 changes: 37 additions & 38 deletions packages/components/src/components/checkbox/checkbox.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
import { DBCheckboxProps, DBCheckboxState } from './model';
import { cls, uuid } from '../../utils';
import {
DEFAULT_ID,
DEFAULT_INVALID_MESSAGE,
DEFAULT_INVALID_MESSAGE_ID_SUFFIX,
DEFAULT_MESSAGE_ID_SUFFIX,
Expand All @@ -29,12 +28,11 @@ export default function DBCheckbox(props: DBCheckboxProps) {
// jscpd:ignore-start
const state = useStore<DBCheckboxState>({
initialized: false,
_id: DEFAULT_ID,
_messageId: DEFAULT_ID + DEFAULT_MESSAGE_ID_SUFFIX,
_validMessageId: DEFAULT_ID + DEFAULT_VALID_MESSAGE_ID_SUFFIX,
_invalidMessageId: DEFAULT_ID + DEFAULT_INVALID_MESSAGE_ID_SUFFIX,
_id: 'checkbox-' + uuid(),
_messageId: this._id + DEFAULT_MESSAGE_ID_SUFFIX,
_validMessageId: this._id + DEFAULT_VALID_MESSAGE_ID_SUFFIX,
_invalidMessageId: this._id + DEFAULT_INVALID_MESSAGE_ID_SUFFIX,
_descByIds: '',

handleChange: (event: ChangeEvent<HTMLInputElement>) => {
if (props.onChange) {
props.onChange(event);
Expand All @@ -44,6 +42,20 @@ export default function DBCheckbox(props: DBCheckboxProps) {
props.change(event);
}
handleFrameworkEvent(this, event, 'checked');

/* For a11y reasons we need to map the correct message with the checkbox */
if (!ref?.validity.valid || props.customValidity === 'invalid') {
state._descByIds = state._invalidMessageId;
} else if (
props.customValidity === 'valid' ||
(ref?.validity.valid && props.required)
) {
state._descByIds = state._validMessageId;
} else if (props.message) {
state._descByIds = state._messageId;
} else {
state._descByIds = '';
}
nmerget marked this conversation as resolved.
Show resolved Hide resolved
},
handleBlur: (event: InteractionEvent<HTMLInputElement>) => {
if (props.onBlur) {
Expand All @@ -62,48 +74,30 @@ export default function DBCheckbox(props: DBCheckboxProps) {
if (props.focus) {
props.focus(event);
}
},
getValidMessage: () => {
return props.validMessage || DEFAULT_VALID_MESSAGE;
},
getInvalidMessage: () => {
return (
props.invalidMessage ||
ref?.validationMessage ||
DEFAULT_INVALID_MESSAGE
);
}
});

onMount(() => {
state.initialized = true;
state._id = props.id || 'checkbox-' + uuid();
state._id = props.id || state._id;
});

onUpdate(() => {
if (state.initialized && state._id) {
state._messageId = state._id + DEFAULT_MESSAGE_ID_SUFFIX;
state._validMessageId = state._id + DEFAULT_VALID_MESSAGE_ID_SUFFIX;
state._invalidMessageId =
if (state._id) {
const messageId = state._id + DEFAULT_MESSAGE_ID_SUFFIX;
const validMessageId = state._id + DEFAULT_VALID_MESSAGE_ID_SUFFIX;
const invalidMessageId =
nmerget marked this conversation as resolved.
Show resolved Hide resolved
state._id + DEFAULT_INVALID_MESSAGE_ID_SUFFIX;
}
}, [state._id, state.initialized]);
state._messageId = messageId;
state._validMessageId = validMessageId;
state._invalidMessageId = invalidMessageId;

onUpdate(() => {
const descByIds = [state._validMessageId, state._invalidMessageId];
if (props.message) {
descByIds.push(state._messageId);
if (props.message) {
state._descByIds = messageId;
}
}
state._descByIds = descByIds.join(' ');
}, [
props.message,
state._messageId,
state._validMessageId,
state._invalidMessageId
]);
// jscpd:ignore-end
}, [state._id]);

// TODO we have to check how to update on every change..
onUpdate(() => {
if (state.initialized && document && state._id) {
const checkboxElement = document?.getElementById(
Expand All @@ -120,9 +114,12 @@ export default function DBCheckbox(props: DBCheckboxProps) {
// It has no accessibility or UX implications. (https://mui.com/material-ui/react-checkbox/)
checkboxElement.indeterminate = props.indeterminate;
}

state.initialized = false;
}
}
}, [state.initialized, props.indeterminate, props.checked]);
// jscpd:ignore-end

return (
<div
Expand Down Expand Up @@ -171,14 +168,16 @@ export default function DBCheckbox(props: DBCheckboxProps) {
id={state._validMessageId}
size="small"
semantic="successful">
{state.getValidMessage()}
{props.validMessage ?? DEFAULT_VALID_MESSAGE}
</DBInfotext>

<DBInfotext
id={state._invalidMessageId}
size="small"
semantic="critical">
{state.getInvalidMessage()}
{props.invalidMessage ??
ref?.validationMessage ??
DEFAULT_INVALID_MESSAGE}
</DBInfotext>
</div>
);
Expand Down
16 changes: 7 additions & 9 deletions packages/components/src/components/checkbox/model.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import {
ChangeEventProps,
ChangeEventState,
FocusEventProps,
FocusEventState,
ChangeEventState,
ChangeEventProps,
GlobalProps,
GlobalState,
FormCheckProps,
FormMessageProps,
FormProps,
FormState,
FormCheckProps,
GlobalProps,
GlobalState,
InitializedState,
FormMessageProps,
FormMessageState,
SizeProps
} from '../../shared/model';

Expand All @@ -37,5 +36,4 @@ export type DBCheckboxState = DBCheckboxDefaultState &
ChangeEventState<HTMLInputElement> &
FocusEventState<HTMLInputElement> &
FormState &
InitializedState &
FormMessageState;
InitializedState;
Loading
Loading